﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma execution_character_set("utf-8")
#define CARD_MODE_INDEX 0
#define LIST_MODE_INDEX 1

#define HAS_CONTENT_INDEX 0
#define NO_CONTENT_GUIDE_INDEX 1
#define NO_PROJECT_GUIDE_INDEX 2

// qt headers
#include <QTabWidget>
#include <QTableView>
#include <QTreeView>
#include <QListView>
#include <QString>
#include <QLabel>
#include <QPushButton>
#include <QHeaderView>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMouseEvent>
#include <QSignalMapper>
#include <QScrollBar>
#include <QTimer>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QComboBox>
#include <QFileDialog >
#include <QApplication>
#include <QMap>
#include <QToolTip>
#include <QPainter>
#include <QWidgetAction>
#include <QItemSelectionModel>
#include <QStackedLayout>
#include <QJsonObject>
#include <QVariant>
#include <QDateTime>

// gcmp headers
#include "LogManager.h"
#include "JournalUtils.h"
#include "CommandParameters.h"
#include "GnufMessageBoardInterface.h"

// custom headers
#include "ProjectPage.h"
#include "ProjectComponentsTableModel.h"
#include "ProjectComponentItemDelegate.h"
#include "ProjectComponentsListModel.h"
#include "ProjectComponentsListItemDelegate.h"
#include "ProjectComponentsTableSymbols.h"
#include "CategoriesTreeModel.h"
#include "ProjectLoadedComponentService.h"
#include "ProjectComponentDetailPanel.h"
#include "SearchTextBox.h"
#include "Bubble.h"
#include "ProjectCloudComponentService.h"
#include "ProjectLocalComponentService.h"
#include "IGmepGcsProjectManager.h"
#include "ProjectComponentsTableHeaderView.h"
#include "AppService.h"
#include "MessageDialog.h"
#include "IDetailedProjectComponent.h"
#include "switcher.h"
#include "CustomReply.h"
#include "UploadWindow.h"

#include "UIService.h"
#include "CustomReply.h"
#include "ProjectCategoryService.h"
#include "PropertyService.h"
#include "IGcsLogger.h"
#include "ListHeaderWidgets.h"
#include "CustomReply.h"
#include "BnufBulletin.h"
#include "ProjectComponentExistingCheck.h"
#include "LoadComponentIntoProjectWorkFlow.h"
#include "PlaceProjectComponentWorkflow.h"
#include "BatchWorkflow.h"
#include "Major.h"
#include "CustomTreeItem.h"
#include "ScrollbarStyles.h"
#include "CategoriesTreeView.h"
#include "PermissionWatcher.h"
#include "Permission.h"
#include "GmepLocalDbService.h"
#include "SyncLocalProjectComponentWorkflow.h"
#include "GcsMainWindow.h"
#include "GbmpNew.h"
#include "FileUtility.h"
#include "JournalUtils.h"
#include "StringUtil.h"
#include "GncINetworkCheck.h"
#include "GapDocumentStatesManager.h"
#include "GmepDocumentStateSetting.h"
#include "GmepVersionUtils.h"
#include "GmepStandardCode.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 

namespace gmepgcs 
{
    ProjectPage::ProjectPage(QWidget * parent, bool isPrivateEnv): QWidget(parent),
        m_projectId(IProjectManager::Get()->GetCurrentProjectId()),
        m_enterpriseId(IProjectManager::Get()->GetCurrentEnterpriseId()),
        m_major(AppService::Get()->GetMajor()),
        m_keyword(),
        m_selectedCategoryCode(L"0"),
        m_currentPage(1),
        m_pageSize(20),
        m_sort(0),
        m_bIsUpdatableOnly(false),
        m_componentSelectionHelper(),
        m_isColloaborationDocument(true),
        m_isPrivateEnv(isPrivateEnv)
    {
        Init();
    };

    ProjectPage::~ProjectPage()
    {

    }

    void ProjectPage::Resume()
    {
        if (m_isColloaborationDocument)
        {
            IProjectManager* pManager = IProjectManager::Get();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pManager != nullptr, L"pManager为空", L"GMEP", L"2024-01-30");
            m_projectId = pManager->GetCurrentProjectId();
            if (m_pContentStackedLayout)
                m_pContentStackedLayout->setCurrentIndex(NO_CONTENT_GUIDE_INDEX);

            if(m_pBtnAdding)
                m_pBtnAdding->setVisible(true);
            if(m_pBtnSyncComponenets)
                m_pBtnSyncComponenets->setVisible(true);

            if (m_pSubOperationRegionAndOthersLayout && m_pBatchOperationLayout)
            {
                if(m_pUpdateSelected)
                    m_pUpdateSelected->setVisible(true);
                if(m_pLoadSelected)
                    m_pLoadSelected->setVisible(true);
                if(m_pDeleteSelected)
                    m_pDeleteSelected->setVisible(true);
                m_pSubOperationRegionAndOthersLayout->addLayout(m_pBatchOperationLayout);
            }
        }
        else
        {
            m_projectId = L"";
            if (m_pBtnAdding)
                m_pBtnAdding->setVisible(false);
            if (m_pBtnSyncComponenets)
                m_pBtnSyncComponenets->setVisible(false);

            if (m_pSubOperationRegionAndOthersLayout && m_pBatchOperationLayout)
            {
                m_pSubOperationRegionAndOthersLayout->removeItem(m_pBatchOperationLayout);
                if (m_pUpdateSelected)
                    m_pUpdateSelected->setVisible(false);
                if (m_pLoadSelected)
                    m_pLoadSelected->setVisible(false);
                if (m_pDeleteSelected)
                    m_pDeleteSelected->setVisible(false);
            }
        }
      
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pDetailPanel != nullptr && m_pSearchTextBox != nullptr && m_pKeyWordBubble != nullptr && m_pCategoryBubble != nullptr
            && m_pIsUpdatableOnlyBubble != nullptr && m_pShowUpdatableOnlySwitcher != nullptr, L"控件指针为空", L"GMEP", L"2024-01-30");
        m_pDetailPanel->setVisible(false);

        m_keyword = std::wstring();
        m_pSearchTextBox->setText(QString());
        m_pKeyWordBubble->SetText(QString::fromStdWString(m_keyword));
        m_componentSelectionHelper.SetCondition(L"keyword", m_keyword);

        m_selectedCategoryCode = L"0";
        m_pCategoryBubble->SetText("全部");
        m_componentSelectionHelper.SetCondition(L"category", m_selectedCategoryCode);

        m_bIsUpdatableOnly = false;
        m_pIsUpdatableOnlyBubble->SetText("否");
        m_pShowUpdatableOnlySwitcher->SetState(m_bIsUpdatableOnly);
        m_componentSelectionHelper.SetCondition(L"isUpdatableOnly", std::to_wstring((int)m_bIsUpdatableOnly));

        m_currentPage = 1;
        RefreshCategoryTree(true);
        RefreshComponentList();
    }

    void ProjectPage::Refresh(bool isCollaborationDocument /*= true*/)
    {       
        // 若协同版和本地版之间切换
        if (m_isColloaborationDocument != isCollaborationDocument)
        {
            m_isColloaborationDocument = isCollaborationDocument;
            if(m_pTableModel)
                m_pTableModel->SetIsEditOperationVisible(m_isColloaborationDocument);
            if(m_headerView)
                m_headerView->SetIsEditOperationVisible(m_isColloaborationDocument);
            if(m_ItemDelegate)
                m_ItemDelegate->SetIsEditOperationVisible(m_isColloaborationDocument);
            if(m_CardItemDelegate)
                m_CardItemDelegate->SetIsEditOperationVisible(m_isColloaborationDocument);
            if(m_cardListHeaderWidget)
                m_cardListHeaderWidget->SetIsEditOperationVisible(m_isColloaborationDocument);
            Resume();
            return;
        }
        // 若协同版 不同项目之前切换
        if (m_isColloaborationDocument)
        {
            IProjectManager* pManager = IProjectManager::Get();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pManager != nullptr, L"pManager为空", L"GMEP", L"2024-01-30");
            std::wstring projectId = pManager->GetCurrentProjectId();
            if (m_projectId != projectId)
            {
                Resume();
                return;
            }
        }
        RefreshCategoryTree(true);
        RefreshComponentList();
    }
    
    void ProjectPage::RefreshComponent(const std::wstring & projectId, const std::wstring & componentId, const int32_t &componentVersion)
    {
        // 若是公有云环境，则需要检查网络
        if (!m_isPrivateEnv)
        {
            gnc::INetworkCheck* pNetwork = gnc::INetworkCheck::Get();
            if(!pNetwork || !pNetwork->CheckNetworkConnection())
                return;
        }
        //DUMP(L"start to refresh single component");
        MainWindow* pMainWnd = UIService::Get()->GetMainWindow();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWnd != nullptr, L"pMainWnd为空", L"GMEP", L"2024-01-30");
        pMainWnd->ShowMask();

        // 更新后若有重新加载族， 可能导致对话框隐藏
        pMainWnd->show();

        if (projectId != m_projectId)
        {
            Refresh(m_isColloaborationDocument);
            pMainWnd->HideMask();
            //DUMP(L"finish refreshing single component");
            return;
        }
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pTableModel != nullptr, L"m_pTableModel为空", L"GMEP", L"2024-01-30");
        if (!m_pTableModel->ExistsComponent(componentId))
        {
            Refresh(m_isColloaborationDocument);
            pMainWnd->HideMask();
            //DUMP(L"finish refreshing single component");
            return;
        }
        m_pTableModel->RefreshComponent();
        
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pDetailPanel != nullptr, L"m_pDetailPanel为空", L"GMEP", L"2024-01-30");
        m_pDetailPanel->setVisible(false);
        pMainWnd->HideMask();

        m_pDetailPanel->setVisible(true);
        std::shared_ptr<IDetailedProjectComponent> spDetailedComponent =
            ProjectLocalComponentService::Get()->GetDetailedComponent(projectId, componentId, componentVersion);


        if (!spDetailedComponent)
        {
            m_pDetailPanel->setVisible(false);
            pMainWnd->HideMask();
            return;
        }
        m_pDetailPanel->SetDetailedComponent(spDetailedComponent);
        m_pDetailPanel->setVisible(true);
        pMainWnd->HideMask();

            spDetailedComponent->SetIsUpdatable(ProjectLoadedComponentService::Get()
                ->IsComponentUpdatable(spDetailedComponent->Id, spDetailedComponent->Version));

            if (AppService::Get()->IsGmep())
            {
                spDetailedComponent->SetIsSyncable(GmepLocalDbService::Get()
                    ->IsComponentSyncable(projectId, spDetailedComponent->Id, spDetailedComponent->Version));
            }

         

            m_pTableModel->RefreshComponent(spDetailedComponent);
            if(m_pListModel)
                m_pListModel->RefreshComponent(spDetailedComponent);
            m_pDetailPanel->SetDetailedComponent(spDetailedComponent);
            m_pDetailPanel->setVisible(true);

            pMainWnd->HideMask();
            //DUMP(L"finish refreshing single component");
        
    }
    

    void ProjectPage::RefreshCategoryTree(bool isBref)
    {

        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pCategoriesTreeView != nullptr, L"m_pCategoriesTreeView为空", L"GMEP", L"2024-01-30");
        if(m_pCategoriesTreeView->Model())
            m_pCategoriesTreeView->Model()->SetCategories({});

        PropertyService* pPropertyService = PropertyService::Get();
        if (!pPropertyService)
        {
            return;
        }

        std::vector<std::shared_ptr<IGmepGcsCategory>> categories = pPropertyService->GetCategories();
        if (!categories.empty())
        {
            RemoveCategoryNoChild(categories);

            std::wstring selectedCategoryCode = m_selectedCategoryCode;
            std::vector<std::wstring> expandedCategoryCodes = m_pCategoriesTreeView->GetExpandedCategoryCodes();
            if(m_pCategoriesTreeView->Model() != nullptr)
                m_pCategoriesTreeView->Model()->SetCategories(categories);
            m_pCategoriesTreeView->SelectCategory(selectedCategoryCode);

            m_pCategoriesTreeView->ExpandCategory(L"0");
            m_pCategoriesTreeView->ExpandCategories(expandedCategoryCodes);
        }
        if(m_pCategoryBubble)
            m_pCategoryBubble->SetText("全部");
    }

    void ProjectPage::RemoveCategoryNoChild(std::vector<std::shared_ptr<IGmepGcsCategory>>& categories)
    {
        if (categories.empty())
            return;

        CategoryNode rootNode;
        rootNode.ChildCount = 0;
        rootNode.pData = categories.at(0);
        BuildCategoryTree(rootNode, categories);

        //GetComponentCount(rootNode);

        std::vector<std::wstring> vecCategoryCode;
        GetComponentCategoryCode(rootNode, vecCategoryCode);
        GetComponentCount(rootNode, vecCategoryCode);

        std::vector<std::shared_ptr<IGmepGcsCategory>> newCategories;
        RemoveCategoryNoChild(rootNode, categories, newCategories);
        
        categories = newCategories;
    }

    void ProjectPage::RemoveCategoryNoChild(const CategoryNode& rootNode, std::vector<std::shared_ptr<IGmepGcsCategory>>& categories, std::vector<std::shared_ptr<IGmepGcsCategory>>& newCategories)
    {
        if (nullptr == rootNode.pData)
            return;

        if (rootNode.ChildCount != 0)
        {
            for (auto &category : categories)
            {
                DBG_WARN_AND_CONTINUE_UNLESS(category != nullptr, L"category为空", L"GMEP", L"2024-01-30");
                if (category->Code == rootNode.pData->Code)
                {
                    category->ComponentCount = rootNode.ChildCount;
                    newCategories.emplace_back(category);
                    break;
                }
            }
        }

        FOR_EACH(item, rootNode.vecNode)
        {
            RemoveCategoryNoChild(item, categories, newCategories);
        }
    }

    void ProjectPage::BuildCategoryTree(CategoryNode& rootNode, const std::vector<std::shared_ptr<IGmepGcsCategory>>& categories)
    {
        if (nullptr == rootNode.pData)
            return;

        for (auto &category : categories)
        {
            if (!category)
            {
                continue;
            }

            if (!category->ParentCode.empty() && category->ParentCode == rootNode.pData->Code)
            {
                CategoryNode node;
                node.ChildCount = 0;
                node.pData = category;
                
                BuildCategoryTree(node, categories);
                rootNode.vecNode.emplace_back(node);
            }
        }
    }

    //此函数性能较差，已经不再使用
    void ProjectPage::GetComponentCount(CategoryNode& rootNode)
    {
        if (nullptr == rootNode.pData)
            return;
        
        if ((int)rootNode.vecNode.size() == 0)
        {
            std::wstring projectId = m_projectId;
            std::wstring categoryCode = rootNode.pData->Code;
            rootNode.ChildCount = ProjectLocalComponentService::Get()->GetProjectComponentCountOfCategory(
                projectId, categoryCode);
            
            return;
        }

        FOR_EACH(item, rootNode.vecNode)
        {
            GetComponentCount(item);

            rootNode.ChildCount += item.ChildCount;
        }
    }

    void ProjectPage::GetComponentCount(CategoryNode& rootNode, const std::vector<std::wstring>& vecCategoryCode)
    {
        std::wstring projectId = m_projectId;
        std::map<std::wstring, int> mapComponentCount;
        ProjectLocalComponentService::Get()->GetProjectComponentCountOfCategory(projectId, vecCategoryCode, mapComponentCount);
        
        CalcComponentCount(rootNode, mapComponentCount);
    }

    void ProjectPage::CalcComponentCount(CategoryNode& rootNode, const std::map<std::wstring, int>& mapComponentCount)
    {
        if (nullptr == rootNode.pData)
            return;

        if ((int)rootNode.vecNode.size() == 0)
        {
            std::wstring categoryCode = rootNode.pData->Code;
            auto itr = mapComponentCount.find(categoryCode);
            if (itr != mapComponentCount.end())
            {
                rootNode.ChildCount = itr->second;
            }
            else
            {
                rootNode.ChildCount = 0;
            }
            return;
        }

        FOR_EACH(item, rootNode.vecNode)
        {
            CalcComponentCount(item, mapComponentCount);

            rootNode.ChildCount += item.ChildCount;
        }
    }

    void ProjectPage::GetComponentCategoryCode(CategoryNode& rootNode, std::vector<std::wstring>& vecCategoryCode)
    {
        if (nullptr == rootNode.pData)
            return;

        if ((int)rootNode.vecNode.size() == 0)
        {            
            vecCategoryCode.emplace_back(rootNode.pData->Code);
            return;
        }

        FOR_EACH(item, rootNode.vecNode)
        {
            GetComponentCategoryCode(item, vecCategoryCode);

            rootNode.ChildCount += item.ChildCount;
        }
    }

    void ProjectPage::GetComponentList(QModelIndex parentIndex, IPagedData<std::vector<std::shared_ptr<IProjectComponent>>>& pagedComponents)
    {
        if (!parentIndex.isValid())
            return;

        const CustomTreeItem* item = static_cast<CustomTreeItem*>(parentIndex.internalPointer());
        if (nullptr != item)
        {
            void* itemData = item->Data(parentIndex.column()).value<void*>();
            if (nullptr != itemData)
            {
                IGmepGcsCategory* category = static_cast<IGmepGcsCategory*>(itemData);
                if (nullptr != category)
                {
                    if (category->IsLeaf())
                    {
                        std::wstring projectId = m_projectId;
                        Major major = m_major;
                        std::wstring keyword = m_keyword;
                        std::wstring categoryCode = category->Code;
                        int32_t page = m_currentPage;
                        int32_t pageSize = m_pageSize;
                        //int32_t sort = m_sort;

                        IPagedData<std::vector<std::shared_ptr<IProjectComponent>>> tmpPagedComponents = ProjectLocalComponentService::Get()->GetComponents(
                            major, keyword, categoryCode, page, pageSize);

                        if (tmpPagedComponents.TotalCount > 0)
                        {
                            pagedComponents.TotalCount += tmpPagedComponents.TotalCount;
                            pagedComponents.Data.insert(pagedComponents.Data.end(), tmpPagedComponents.Data.begin(), tmpPagedComponents.Data.end());
                        }
                    }
                }
            }
        }
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pCategoriesTreeView != nullptr, L"m_pCategoriesTreeView为空", L"GMEP", L"2024-01-30");
        CategoriesTreeModel *pModel = m_pCategoriesTreeView->Model();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pModel != nullptr, L"pModel为空", L"GMEP", L"2024-01-30");
        int rowCount = pModel->rowCount(parentIndex);
        for (int i = 0; i < rowCount; i++)
        {
            QModelIndex childIndex = pModel->index(i, 0, parentIndex);
            GetComponentList(childIndex, pagedComponents);
        }
    }

    void ProjectPage::RefreshComponentList()
    {
        //DUMP(L"start to refresh project component list");
        MainWindow* pMainWnd = UIService::Get()->GetMainWindow();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWnd != nullptr, L"pMainWnd为空", L"GMEP", L"2024-01-30");
        pMainWnd->ShowMask();
        

        std::wstring projectId = m_projectId;
        Major major = m_major;
        std::wstring keyword = m_keyword;

        // not updatable only GMEP
        {
            IPagedData<std::vector<std::shared_ptr<IProjectComponent>>> pagedComponents = IPagedData<std::vector<std::shared_ptr<IProjectComponent>>>();
            pagedComponents.TotalCount = 0;
            pagedComponents.Data = std::vector<std::shared_ptr<IProjectComponent>>();

            if (!m_pCategoriesTreeView)
                return;
            QModelIndex currentIndex = m_pCategoriesTreeView->currentIndex();
            if (currentIndex.isValid())
            {
                GetComponentList(currentIndex, pagedComponents);
            }
            else if(m_pCategoriesTreeView->Model())
            {
                QModelIndex index = m_pCategoriesTreeView->Model()->FindIndex(L"0");
                GetComponentList(index, pagedComponents);
            }
            if (pagedComponents.TotalCount > 1)
            {
                // 20230630 暂时按名称排序,新增的gac在最上面
                std::sort(pagedComponents.Data.begin(), pagedComponents.Data.end(), [](const std::shared_ptr<IProjectComponent> & pProjectComponent1, const std::shared_ptr<IProjectComponent> & pProjectComponent2) {
                    DBG_WARN_AND_RETURN_FALSE_UNLESS(pProjectComponent1, L"pProjectComponent1为空", L"GMEP", L"2024-01-30");
                    DBG_WARN_AND_RETURN_FALSE_UNLESS(pProjectComponent2, L"pProjectComponent2为空", L"GMEP", L"2024-01-30");

                    if (pProjectComponent1->TimeStamp != pProjectComponent2->TimeStamp)
                        return pProjectComponent1->TimeStamp > pProjectComponent2->TimeStamp;
                    else
                        return pProjectComponent1->Name < pProjectComponent2->Name;
                });
            }
            
            if (0 == pagedComponents.TotalCount)
            {
                if(m_pTableModel)
                    m_pTableModel->SetComponents({});
                if(m_pListModel)
                    m_pListModel->SetComponents({});
                if(m_pLblResultCount)
                    m_pLblResultCount->setText("0");
                pMainWnd->HideMask();
                //DUMP(L"finish refreshing project component list");
                return;
            }

            // Get max version in local db3
            std::wstring strIdRange;
            for (auto &spProjectComponent : pagedComponents.Data)
            {
                if (!spProjectComponent)
                {
                    continue;
                }
                std::wstring strId = spProjectComponent->Id;
                if(strId.empty())
                    continue;

                if (strIdRange.empty())
                    strIdRange = strId;
                else
                    strIdRange += L"," + strId;
            }

           std::map<std::wstring, int> compIdVersionInLocalMap = GmepLocalDbService::Get()
                ->GetComponentsMaxVersion(projectId, strIdRange);


            for (auto &spProjectComponent : pagedComponents.Data)
            {
                if (!spProjectComponent)
                {
                    continue;
                }

                spProjectComponent->SetIsUpdatable(ProjectLoadedComponentService::Get()
                    ->IsComponentUpdatable(spProjectComponent->Id, spProjectComponent->Version));

                    bool isSyncable = false;
                    if (compIdVersionInLocalMap.find(spProjectComponent->Id) != compIdVersionInLocalMap.end())
                    {
                        int iLocalVersion = compIdVersionInLocalMap[spProjectComponent->Id];
                        isSyncable = iLocalVersion < spProjectComponent->Version;
                    }
                    spProjectComponent->SetIsSyncable(isSyncable);                    


                
            }

            if (pagedComponents.TotalCount == 0 &&
                m_selectedCategoryCode == L"0" &&
                m_keyword.empty() && m_pContentStackedLayout)
            {
                m_pContentStackedLayout->setCurrentIndex(NO_CONTENT_GUIDE_INDEX);
            }
            else if(m_pContentStackedLayout)
            {
                m_pContentStackedLayout->setCurrentIndex(HAS_CONTENT_INDEX);
            }

            if(m_pTableModel)
                m_pTableModel->SetComponents(pagedComponents.Data);
            if(m_pListModel)
                m_pListModel->SetComponents(pagedComponents.Data);
            if(m_pLblResultCount)
                m_pLblResultCount->setText(QString::number(pagedComponents.TotalCount));
            //m_pPager->ChangeTotalCount((int32_t)pagedComponents.TotalCount, page); // 所有总数

            pMainWnd->HideMask();
            //DUMP(L"finish refreshing project component list");
        }
    }


    bool ProjectPage::eventFilter(QObject * obj, QEvent * event)
    {
        DBG_WARN_AND_RETURN_FALSE_UNLESS(event != nullptr, L"event为空", L"GMEP", L"2024-01-30");
        switch (event->type()) 
        {
        case QEvent::KeyPress: 
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            if (keyEvent && keyEvent->key() == Qt::Key_Return)
            {
                m_componentSelectionHelper.SetCondition(L"keyword", m_keyword);
                if(m_pKeyWordBubble)
                    m_pKeyWordBubble->SetText(QString::fromStdWString(m_keyword));

                m_selectedCategoryCode = L"0";
                if(m_pCategoryBubble)
                    m_pCategoryBubble->SetText("全部");

                m_currentPage = 1;

                RefreshCategoryTree();
                RefreshComponentList();
                return true;
            }
            break;
        }
        case QEvent::Enter:
        {
            QPushButton* pBtn= dynamic_cast<QPushButton *>(obj);
            if (m_pBtnAdding != nullptr && pBtn == m_pBtnAdding)
            {
                if(m_pMenuAdding)
                    m_pMenuAdding->popup(mapToGlobal(m_pBtnAdding->geometry().bottomLeft()));
            }
            break;
        }        
        case QEvent::Leave:
        {
            break;
        }
        case QEvent::MouseButtonPress:
        {
            QPushButton * pBtn = dynamic_cast<QPushButton *>(obj);
            if (m_pBtnAdding != nullptr && pBtn == m_pBtnAdding && m_pMenuAdding != nullptr)
            {
                m_pMenuAdding->popup(mapToGlobal(m_pBtnAdding->geometry().bottomLeft()));
            }
            break;
        }
        default: 
        {
            break;
        }
        }

        return QWidget::eventFilter(obj, event);
    }

    void ProjectPage::Init()
    {
        // project page self
        QPalette palette;
        palette.setColor(QPalette::Background, QColor(240, 240, 245, 255)); 
        setAutoFillBackground(true);
        setPalette(palette);

        // main layout
        QHBoxLayout *pMainLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pMainLayout != nullptr, L"pMainLayout为空", L"GMEP", L"2024-01-30");
        setLayout(pMainLayout);
        pMainLayout->setSpacing(0);
        pMainLayout->setContentsMargins(0, 0, 0, 0);

        // 主要操作区域及其下面部分
        QVBoxLayout *pMainOperationRegionAndOthersLayout = NEW_AS_QT(QVBoxLayout);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pMainOperationRegionAndOthersLayout != nullptr, L"pMainOperationRegionAndOthersLayout为空", L"GMEP", L"2024-01-30");
        pMainLayout->addLayout(pMainOperationRegionAndOthersLayout);
        pMainOperationRegionAndOthersLayout->setSpacing(0);
        pMainOperationRegionAndOthersLayout->setContentsMargins(0, 0, 0, 0);

        // 主操作区域
        QFrame *pMainOperationRegionFrame = NEW_AS_QT(QFrame,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pMainOperationRegionFrame != nullptr, L"pMainOperationRegionFrame为空", L"GMEP", L"2024-01-30");
        pMainOperationRegionFrame->setObjectName("MainOperationRegionFrame");
        pMainOperationRegionAndOthersLayout->addWidget(pMainOperationRegionFrame);
        pMainOperationRegionFrame->setStyleSheet(QString::fromStdWString(L"QFrame#%1{border-bottom:1px solid rgb(210,215,220); }").arg(pMainOperationRegionFrame->objectName()));
        QLayout * pMainOperationRegionLayout = InitMainOperationRegion(pMainOperationRegionFrame);
        pMainOperationRegionFrame->setLayout(pMainOperationRegionLayout);
        
        // 分类树及其右侧部分
        QHBoxLayout *pCategoryTreeAndOthersLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pCategoryTreeAndOthersLayout != nullptr, L"pCategoryTreeAndOthersLayout为空", L"GMEP", L"2024-01-30");
        pMainOperationRegionAndOthersLayout->addLayout(pCategoryTreeAndOthersLayout);
        pCategoryTreeAndOthersLayout->setSpacing(0);
        pCategoryTreeAndOthersLayout->setContentsMargins(0, 0, 0, 0);
        
        // 分类树
        QLayout * pCategoryTreeLayout = InitCategoryTree(this);
        pCategoryTreeAndOthersLayout->addLayout(pCategoryTreeLayout);

        // 次级操作区域及其下侧部分
        m_pSubOperationRegionAndOthersLayout = NEW_AS_QT(QVBoxLayout);
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pSubOperationRegionAndOthersLayout != nullptr, L"m_pSubOperationRegionAndOthersLayout为空", L"GMEP", L"2024-01-30");
        pCategoryTreeAndOthersLayout->addLayout(m_pSubOperationRegionAndOthersLayout);
        m_pSubOperationRegionAndOthersLayout->setSpacing(0);
        m_pSubOperationRegionAndOthersLayout->setContentsMargins(0, 0, 0, 0);

        // 子操作区域
        QFrame *pSubOperationRegionFrame = NEW_AS_QT(QFrame,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pSubOperationRegionFrame != nullptr, L"pSubOperationRegionFrame为空", L"GMEP", L"2024-01-30");
        pSubOperationRegionFrame->setObjectName("SubOperationRegionFrame");
        m_pSubOperationRegionAndOthersLayout->addWidget(pSubOperationRegionFrame);
        pSubOperationRegionFrame->setStyleSheet(QString::fromStdWString(L"QFrame#%1{border-top:1px solid rgb(210,215,220);}").arg(pSubOperationRegionFrame->objectName()));
        QLayout * PSubOperationRegionLayout = InitSubOperationRegion(pSubOperationRegionFrame);
        if (PSubOperationRegionLayout)
        {
            PSubOperationRegionLayout->setContentsMargins(8, 8, 8, 8);
            pSubOperationRegionFrame->setLayout(PSubOperationRegionLayout);
        }

        // 层叠：构件集合及其分页区域、无构件引导页、无协同项目引导页
        m_pContentStackedLayout = NEW_AS_QT(QStackedLayout);
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pContentStackedLayout != nullptr, L"m_pContentStackedLayout为空", L"GMEP", L"2024-01-30");
        m_pContentStackedLayout->setStackingMode(QStackedLayout::StackingMode::StackOne);
        m_pSubOperationRegionAndOthersLayout->addLayout(m_pContentStackedLayout);

        // 构件集合及其分页区域
        QWidget *pContentAndPagerWidget = NEW_AS_QT(QWidget,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pContentAndPagerWidget != nullptr, L"pContentAndPagerWidget为空", L"GMEP", L"2024-01-30");
        m_pContentStackedLayout->addWidget(pContentAndPagerWidget);

        QVBoxLayout *pContentAndPagerLayout = NEW_AS_QT(QVBoxLayout);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pContentAndPagerLayout != nullptr, L"pContentAndPagerLayout为空", L"GMEP", L"2024-01-30");
        pContentAndPagerWidget->setLayout(pContentAndPagerLayout);
        pContentAndPagerLayout->setSpacing(0);
        pContentAndPagerLayout->setContentsMargins(0, 0, 0, 0);
        
        // 构件集合展示区域
        QWidget* pViewWidget = NEW_AS_QT(QWidget,pContentAndPagerWidget);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pViewWidget != nullptr, L"pViewWidget为空", L"GMEP", L"2024-01-30");
        pContentAndPagerLayout->addWidget(pViewWidget);

        m_pViewLayout = NEW_AS_QT(QStackedLayout);
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pViewLayout != nullptr, L"m_pViewLayout为空", L"GMEP", L"2024-01-30");
        pViewWidget->setLayout(m_pViewLayout);
                
        QWidget *cardModeView = NEW_AS_QT(QWidget,pViewWidget); // 卡片模式
        DBG_WARN_AND_RETURN_VOID_UNLESS(cardModeView != nullptr, L"cardModeView为空", L"GMEP", L"2024-01-30");
        cardModeView->setObjectName("CardModeViewBaseWidget");
        cardModeView->setContentsMargins(0, 0, 0, 0);
        QLayout * pComponentCardListLayout = InitComponentCardList(cardModeView);
        cardModeView->setLayout(pComponentCardListLayout);
        m_pViewLayout->addWidget(cardModeView);
       
        QWidget * tableModeView = NEW_AS_QT(QWidget,pViewWidget); // 列表模式
        DBG_WARN_AND_RETURN_VOID_UNLESS(tableModeView != nullptr, L"tableModeView为空", L"GMEP", L"2024-01-30");

        tableModeView->setObjectName("TableModeViewBaseWidget");
        tableModeView->setStyleSheet(QString::fromStdWString(L"QWidget{border:none;}"));
        QLayout * pComponentTableLayout = InitComponentTable(tableModeView);
        if(pComponentTableLayout)
            tableModeView->setLayout(pComponentTableLayout);
        m_pViewLayout->addWidget(tableModeView);
        m_pViewLayout->setCurrentIndex(LIST_MODE_INDEX);
        
        // 批量操作按钮组 (更新，删除)
        m_pBatchOperationLayout = InitBatchOperationButtonGroup(pContentAndPagerWidget);
        if(m_pBatchOperationLayout)
            m_pSubOperationRegionAndOthersLayout->addLayout(m_pBatchOperationLayout);
        //m_pSubOperationRegionAndOthersLayout->removeItem(m_pBatchOperationLayout);

        // 有协同项目、无构件内容引导区域
        QWidget *  pNoContentGuideView = NEW_AS_QT(QWidget,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pNoContentGuideView != nullptr, L"pNoContentGuideView为空", L"GMEP", L"2024-01-30");
        pNoContentGuideView->setObjectName("NoContentGuideWidget");
        pNoContentGuideView->setContentsMargins(0, 0, 0, 0);

        QLayout * pNoContentGuideViewLayout = InitNoContentGuideView(pNoContentGuideView);
        if(pNoContentGuideViewLayout)
            pNoContentGuideView->setLayout(pNoContentGuideViewLayout);
        m_pContentStackedLayout->addWidget(pNoContentGuideView);

        // 无协同项目引导区域
        QWidget * pNoProjectGuideView = NEW_AS_QT(QWidget,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pNoProjectGuideView != nullptr, L"pNoProjectGuideView为空", L"GMEP", L"2024-01-30");
        m_pContentStackedLayout->addWidget(pNoProjectGuideView);
        pNoProjectGuideView->setObjectName("NoProjectGuideWidget");
        pNoProjectGuideView->setContentsMargins(0, 0, 0, 0);

        QLayout * pNoProjectGuideViewLayout = InitNoProjectGuideView(pNoProjectGuideView);
        if(pNoProjectGuideViewLayout)
            pNoProjectGuideView->setLayout(pNoProjectGuideViewLayout);

        if(!m_projectId.empty())
        {
            m_pContentStackedLayout->setCurrentIndex(NO_CONTENT_GUIDE_INDEX);
        }
        
        // 详情面板
        QLayout * pDetailPanelLayout = InitComponentDetailRegion(this);
        if(pDetailPanelLayout)
            pMainLayout->addLayout(pDetailPanelLayout);
    }

    QLayout * ProjectPage::InitCategoryTree(QWidget * parent)
    {
        m_pCategoriesTreeView = NEW_AS_QT(CategoriesTreeView,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pCategoriesTreeView != nullptr, L"m_pCategoriesTreeView为空", L"GMEP", L"2024-01-30");
        m_pCategoriesTreeView->setObjectName("CategoryTreeView");
        m_pCategoriesTreeView->installEventFilter(this);
        
        connect(m_pCategoriesTreeView, &CategoriesTreeView::CategoryClicked, [=](IGmepGcsCategory *pCategory) {
            if (!pCategory)
            {
                return;
            }

            m_selectedCategoryCode = pCategory->Code;
            if(m_pCategoryBubble)
                m_pCategoryBubble->SetText(QString::fromStdWString(pCategory->Name));
            m_componentSelectionHelper.SetCondition(L"category", m_selectedCategoryCode);

            m_currentPage = 1;

            //RefreshCategoryTree();
            RefreshComponentList();
        });

        QHBoxLayout *pLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLayout != nullptr, L"pLayout为空", L"GMEP", L"2024-01-30");
        pLayout->setContentsMargins(0, 0, 0, 0);
        pLayout->setSpacing(0);
        pLayout->addWidget(m_pCategoriesTreeView);

        return pLayout;
    }

    QLayout * ProjectPage::InitComponentTable(QWidget * parent)
    {
        // component tableview
        m_pTableView = NEW_AS_QT(QTableView,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pTableView != nullptr, L"m_pTableView为空", L"GMEP", L"2024-01-30");
        m_pTableView->setObjectName("ComponentTableView");
        m_pTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
        m_pTableView->setSelectionMode(QAbstractItemView::SingleSelection);
        if(m_pTableView->verticalHeader())
            m_pTableView->verticalHeader()->hide();
        m_pTableView->setStyleSheet("border:none");
        m_pTableView->setMouseTracking(true);

        // component table model
        m_pTableModel = NEW_AS_QT(ProjectComponentsTableModel);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pTableModel != nullptr, L"m_pTableModel为空", L"GMEP", L"2024-01-30");
        m_pTableModel->SetIsPrivateEnv(m_isPrivateEnv);
        m_pTableView->setModel(m_pTableModel);
        connect(m_pTableModel, &ProjectComponentsTableModel::ComponentCheckStateChanged,
            [=](IProjectComponent *component, const bool &checked) {
            if (!component)
            {
                return;
            }

            if (checked)
            {
                m_componentSelectionHelper.Add(component->Id, *component);
            }
            else
            {
                m_componentSelectionHelper.Remove(component->Id);
            }
            if(m_pListModel)
                m_pListModel->ChangeCheckState(component, checked);
        });
        connect(m_pTableModel, &ProjectComponentsTableModel::TableUpdated, [=]() {
            for (auto &item : m_componentSelectionHelper.GetSelectedItems())
            {
                m_pTableModel->ChangeCheckState(&item, true);
            }
        });
        PermissionWatcher::Get()->OnCurrentPermissionChanged([=](const int oldPermission, const int newPermission) {
            emit m_pTableModel->layoutChanged();
        });

        // component table delegate
        m_ItemDelegate = NEW_AS_QT(ProjectComponentItemDelegate,this);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_ItemDelegate != nullptr, L"delegate为空", L"GMEP", L"2024-01-30");
        m_pTableView->setItemDelegate(m_ItemDelegate);
        connect(m_ItemDelegate, &ProjectComponentItemDelegate::LoadComponentRequired, this, &ProjectPage::HandleLoadComponentIntoProject);
        connect(m_ItemDelegate, &ProjectComponentItemDelegate::SyncComponentRequired, this, &ProjectPage::HandleSyncComponent);
        connect(m_ItemDelegate, &ProjectComponentItemDelegate::DeleteComponentRequired, this, &ProjectPage::HandleDeleteComponentLocal);
        connect(m_ItemDelegate, &ProjectComponentItemDelegate::PlaceComponentRequired, this, &ProjectPage::HandlePlaceComponent);

        // component table header view
        m_headerView = NEW_AS_QT(ProjectComponentsTableHeaderView,Qt::Horizontal, parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_headerView != nullptr, L"headerView为空", L"GMEP", L"2024-01-30");
        m_headerView->setObjectName("ComponentTableHeaderView");
        m_pTableView->setHorizontalHeader(m_headerView);
        m_headerView->setSectionResizeMode(COL_CHECKBOX, QHeaderView::ResizeToContents);
        m_headerView->setSectionResizeMode(COL_COMPONENT_NAME, QHeaderView::Stretch);
        m_headerView->setSectionResizeMode(COL_COMPONENT_OPERARION_SYNC, QHeaderView::Fixed);
        m_headerView->setSectionResizeMode(COL_COMPONENT_OPERARION_PLACE, QHeaderView::Fixed);
        m_headerView->setSectionResizeMode(COL_COMPONENT_OPERARION_LOADINTO, QHeaderView::Fixed);
        m_headerView->setSectionResizeMode(COL_COMPONENT_OPERARION_DELETE, QHeaderView::Fixed);

        if(m_pTableView->verticalHeader())
            m_pTableView->verticalHeader()->setDefaultSectionSize(48);
        m_pTableView->setColumnHidden(COL_COMPONENT_STATUS, true);
        m_pTableView->setShowGrid(false);
        m_pTableView->setColumnWidth(COL_COMPONENT_OPERARION_SYNC, 50);
        m_pTableView->setColumnWidth(COL_COMPONENT_OPERARION_PLACE, 50);
        m_pTableView->setColumnWidth(COL_COMPONENT_OPERARION_LOADINTO, 50);
        m_pTableView->setColumnWidth(COL_COMPONENT_OPERARION_DELETE, 100);

        m_pTableView->setAttribute(Qt::WA_TranslucentBackground, true);
        if (m_pTableView->verticalScrollBar())
            m_pTableView->verticalScrollBar()->setStyleSheet(ScrollBarStyles::VerticalScrollBarStyle());
        if(m_pTableView->horizontalScrollBar())
            m_pTableView->horizontalScrollBar()->setStyleSheet(ScrollBarStyles::HorizontalScrollBarStyle());

        connect(m_pTableModel, &ProjectComponentsTableModel::CheckStatesChanged, m_headerView, &ProjectComponentsTableHeaderView::ChangeCheckState);
        connect(m_headerView, &ProjectComponentsTableHeaderView::CheckStateChanged, m_pTableModel, &ProjectComponentsTableModel::ChangeCheckStates);

        connect(m_pTableView->selectionModel(), &QItemSelectionModel::selectionChanged,
            [=](const QItemSelection &selected, const QItemSelection &deselected) {HandleComponentSelectionChanged(selected, deselected); });
        connect(m_pTableView, &QTableView::doubleClicked, [=](const QModelIndex &index) {HandlePlaceComponent(m_pTableModel, index); });

        QHBoxLayout* pLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLayout != nullptr, L"pLayout为空", L"GMEP", L"2024-01-30");
        pLayout->setContentsMargins(0, 0, 0, 0);
        pLayout->setSpacing(0);
        pLayout->addWidget(m_pTableView);
        return pLayout;
    }

    QLayout * ProjectPage::InitComponentCardList(QWidget * parent)
    {
        // component list view
        QListView *listView = NEW_AS_QT(QListView,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(listView != nullptr, L"listView为空", L"GMEP", L"2024-01-30");
        listView->setObjectName("ComponentCardListView");
        listView->setViewMode(QListView::IconMode);
        listView->setResizeMode(QListView::Adjust);
        listView->setSelectionMode(QListView::SingleSelection);
        listView->setSpacing(13);
        listView->setWordWrap(true);
        listView->setUpdatesEnabled(true);
        listView->setEditTriggers(QListView::NoEditTriggers);
        listView->setMouseTracking(true);
        listView->setStyleSheet(
            "QListView{"
            "border:none;"
            "background:transparent;"
            "}"
        );
        listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        if(listView->verticalScrollBar())
            listView->verticalScrollBar()->setStyleSheet(ScrollBarStyles::VerticalScrollBarStyle());
        if(listView->horizontalScrollBar())
            listView->horizontalScrollBar()->setStyleSheet(ScrollBarStyles::HorizontalScrollBarStyle());

        // component list model
        m_pListModel = NEW_AS_QT (ListItemModel);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pListModel != nullptr, L"m_pListModel为空", L"GMEP", L"2024-01-30");
        listView->setModel(m_pListModel);
        connect(m_pListModel, &ListItemModel::ComponentCheckStateChanged,
            [=](IProjectComponent *component, const bool &checked) {
            if (!component)
            {
                return;
            }

            if (checked)
            {
                m_componentSelectionHelper.Add(component->Id, *component);
            }
            else
            {
                m_componentSelectionHelper.Remove(component->Id);
            }

            if(m_pTableModel)
                m_pTableModel->ChangeCheckState(component, checked);
        });
        connect(m_pListModel, &ListItemModel::ListUpdated, [=]() {
            for (auto &item : m_componentSelectionHelper.GetSelectedItems())
            {
                m_pListModel->ChangeCheckState(&item, true);
            }
        });
        PermissionWatcher::Get()->OnCurrentPermissionChanged([=](const int oldPermission, const int newPermission) {
            emit m_pListModel->layoutChanged();
        });

        // component list delegate
        m_CardItemDelegate = NEW_AS_QT (ListItemDelegate,this);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_CardItemDelegate != nullptr, L"listDelegate为空", L"GMEP", L"2024-01-30");
        listView->setItemDelegate(m_CardItemDelegate);
        connect(m_CardItemDelegate, &ListItemDelegate::LoadComponentRequired, this, &ProjectPage::HandleLoadComponentIntoProject);
        connect(m_CardItemDelegate, &ListItemDelegate::SyncComponentRequired, this, &ProjectPage::HandleSyncComponent);
        connect(m_CardItemDelegate, &ListItemDelegate::DeleteComponentRequired, this, &ProjectPage::HandleDeleteComponentLocal);
        connect(m_CardItemDelegate, &ListItemDelegate::PlaceComponentRequired, this, &ProjectPage::HandlePlaceComponent);

        // component list header
        m_cardListHeaderWidget = NEW_AS_QT(ListHeaderWidget,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_cardListHeaderWidget != nullptr, L"listHeaderWidget为空", L"GMEP", L"2024-01-30");
        m_cardListHeaderWidget->setObjectName("ComponentCardListHeaderWidget");
        m_cardListHeaderWidget->setStyleSheet("QWidget{background:#D7DDE6;}");
        m_cardListHeaderWidget->setFixedHeight(24);
        m_cardListHeaderWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
        
        connect(m_pListModel, &ListItemModel::CheckStatesChanged, m_cardListHeaderWidget, &ListHeaderWidget::ChangeCheckState);
        connect(m_cardListHeaderWidget, &ListHeaderWidget::CheckStateChanged, m_pListModel, &ListItemModel::ChangeCheckStates);

        connect(listView->selectionModel(), &QItemSelectionModel::selectionChanged,
            [=](const QItemSelection &selected, const QItemSelection &deselected) {HandleComponentSelectionChanged(selected, deselected); });
        connect(listView, &QListView::doubleClicked, [=](const QModelIndex &index) {HandlePlaceComponent(m_pListModel, index); });

        QVBoxLayout *listLayout = NEW_AS_QT (QVBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(listLayout != nullptr, L"listLayout为空", L"GMEP", L"2024-01-30");
        listLayout->setSpacing(0);
        listLayout->setContentsMargins(0, 0, 0, 0);
        listLayout->setMargin(0);
        listLayout->addWidget(m_cardListHeaderWidget);
        listLayout->addWidget(listView);

        return listLayout;
    }

    QLayout * ProjectPage::InitMainOperationRegion(QWidget * parent)
    {
        QHBoxLayout *pMainOperationLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pMainOperationLayout != nullptr, L"pMainOperationLayout为空", L"GMEP", L"2024-01-30");
        pMainOperationLayout->setContentsMargins(QMargins(8, 8, 8, 8));
        pMainOperationLayout->setSpacing(8);

        // 添加构件入口按钮组 （本地文档隐藏）
        QLayout* PAddComponentButtonGroupLayout = InitAddComponentButtonGroup(parent);
        if (PAddComponentButtonGroupLayout)
            pMainOperationLayout->addLayout(PAddComponentButtonGroupLayout);

        // 一键同步按钮（本地文档隐藏）        
        m_pBtnSyncComponenets = NEW_AS_QT( QPushButton,QString::fromStdWString(GBMP_TR(L"一键同步")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pBtnSyncComponenets != nullptr, L"pBtnSyncComponenets为空", L"GMEP", L"2024-01-30");
        m_pBtnSyncComponenets->setObjectName("UpdateGmepLocalComponentLibraryWithOneKeyButton");
        pMainOperationLayout->addWidget(m_pBtnSyncComponenets);
        m_pBtnSyncComponenets->setFixedHeight(24);
        m_pBtnSyncComponenets->setFixedWidth(80);
        m_pBtnSyncComponenets->setCursor(QCursor(Qt::CursorShape::PointingHandCursor));
        m_pBtnSyncComponenets->setStyleSheet(
            "QPushButton{"
            "background-color:rgba(82,145,221,255);"
            "border:1px solid rgba(82,145,221,255);"
            "border-radius:2px;"
            "font-family:Microsoft YaHei;"
            "font-size:12px;"
            "color:rgba(255,255,255,255);"
            "padding:0px;"
            "}");
        connect(m_pBtnSyncComponenets, &QPushButton::clicked, [=]() {
            // 若是公有云环境，则需要检查网络
            if (!m_isPrivateEnv)
            {
                gnc::INetworkCheck* pNetwork = gnc::INetworkCheck::Get();
                if (!pNetwork || !pNetwork->CheckNetworkConnection())
                {
                    BnufBulletin::SingleShot(GBMP_TR(L"没有联网，请检查网络"), BnufBulletin::BulletinIcon::Error);
                    return;
                }
            }
            std::wstring projectId = m_projectId;
            std::wstring enterpriseId = m_enterpriseId;
            Major major = m_major;

            // 找出本地新增的，修改的，删除的, 下载，merge，上传
            const std::vector<IProjectComponent> compVec;
            std::wstring strSuccInfo = L"";
            bool hasChange = ProjectLocalComponentService::Get()->SyncComponents(projectId, enterpriseId, compVec, strSuccInfo);


            if (hasChange) // 若无更新则不需要刷新列表
            {
                RefreshCategoryTree();
                RefreshComponentList();
            }

            if (!strSuccInfo.empty())
            {
               gnuf::ShowMessageBoard(winId(), strSuccInfo, GBMP_TR(L"提示"), gnuf::eMB_OK | gnuf::eMB_ICONINFORMATION);
            }
            
            return;
           
        });

       

        // stretch
        pMainOperationLayout->addStretch();
        
        // 搜索框
        m_pSearchTextBox = NEW_AS_QT( SearchTextBox,QString::fromStdWString(GBMP_TR(L"输入关键词或短语查找构件")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pSearchTextBox != nullptr, L"m_pSearchTextBox为空", L"GMEP", L"2024-01-30");
        m_pSearchTextBox->setObjectName("SearchLineEdit");
        pMainOperationLayout->addWidget(m_pSearchTextBox);
        m_pSearchTextBox->installEventFilter(this);
        m_pSearchTextBox->setFixedWidth(200);
        connect(m_pSearchTextBox, &QLineEdit::textChanged, [=](const QString &text) {
            m_keyword = text.toStdWString();
        });
        connect(m_pSearchTextBox, &SearchTextBox::Searched, [=]() {
            if(m_pKeyWordBubble)
                m_pKeyWordBubble->SetText(QString::fromStdWString(m_keyword));
            m_componentSelectionHelper.SetCondition(L"keyword", m_keyword);

            m_selectedCategoryCode = L"0";
            if(m_pCategoryBubble)
                m_pCategoryBubble->SetText("全部");

            m_bIsUpdatableOnly = false;
            if(m_pIsUpdatableOnlyBubble)
                m_pIsUpdatableOnlyBubble->SetText("否");
            if(m_pShowUpdatableOnlySwitcher)
                m_pShowUpdatableOnlySwitcher->SetState(m_bIsUpdatableOnly);

            m_currentPage = 1;

            RefreshCategoryTree();
            RefreshComponentList();
        });

        // 列表/卡片模式切换按钮组
        QLayout* pSwitchModeButtonGroupLayout = InitModeSwitchButtonGroup(parent);
        if(pSwitchModeButtonGroupLayout)
            pMainOperationLayout->addLayout(pSwitchModeButtonGroupLayout);
        
        return pMainOperationLayout;
    }

    QLayout * ProjectPage::InitBatchOperationButtonGroup(QWidget * parent)
    {
        QHBoxLayout *pBatchOperationLayout = NEW_AS_QT (QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pBatchOperationLayout != nullptr, L"pBatchOperationLayout为空", L"GMEP", L"2024-01-30");
        pBatchOperationLayout->setSpacing(5);
        pBatchOperationLayout->setMargin(5);

        // 批量更新按钮
        m_pUpdateSelected = NEW_AS_QT( QPushButton,QString::fromStdWString(GBMP_TR(L"同步")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pUpdateSelected != nullptr, L"m_pUpdateSelected为空", L"GMEP", L"2024-01-30");
        m_pUpdateSelected->setObjectName("UpdateInBatchButton");
        pBatchOperationLayout->addWidget(m_pUpdateSelected);
        m_pUpdateSelected->setEnabled(false);
        connect(&m_componentSelectionHelper, &SelectionHelper<IProjectComponent>::ItemsChanged, [=]() {
            m_pUpdateSelected->setEnabled(!m_componentSelectionHelper.Empty());
        });
        m_pUpdateSelected->setFixedWidth(56);
        m_pUpdateSelected->setFixedHeight(24);
        m_pUpdateSelected->setCursor(Qt::PointingHandCursor);
        m_pUpdateSelected->setStyleSheet(
            "QPushButton{"
            "background:rgba(255,255,255,255);"
            "color:#596370;"
            "border-top-left-radius:2px;"
            "border-top-right-radius:0px;"
            "border-bottom-right-radius:0px;"
            "border-bottom-left-radius:2px;"
            "border:1px solid rgba(217, 221, 228, 255);"
            "}"
            "QPushButton:disabled{"
            "background:rgba(240, 240, 245,255);"
            "color:rgba(169, 176, 189, 255);"
            "}"
        );
        connect(m_pUpdateSelected, &QPushButton::clicked, [=]() {
            // 若是公有云环境，则需要检查网络
            if (!m_isPrivateEnv && (!gnc::INetworkCheck::Get() || !gnc::INetworkCheck::Get()->CheckNetworkConnection()))
            {
                BnufBulletin::SingleShot(GBMP_TR(L"没有联网，请检查网络"), BnufBulletin::BulletinIcon::Error);
            }
            else
            {
               SyncComponentInBatch();
            }
        });

        // 批量载入按钮
        m_pLoadSelected = NEW_AS_QT( QPushButton,QString::fromStdWString(GBMP_TR(L"载入")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pLoadSelected != nullptr, L"m_pLoadSelected为空", L"GMEP", L"2024-01-30");
        m_pLoadSelected->setObjectName("LoadInBatchButton");
        pBatchOperationLayout->addWidget(m_pLoadSelected);
        m_pLoadSelected->setEnabled(false);
        connect(&m_componentSelectionHelper, &SelectionHelper<IProjectComponent>::ItemsChanged, [=]() {
            m_pLoadSelected->setEnabled(
                (!m_componentSelectionHelper.Empty()) &&
                m_componentSelectionHelper.All([=](IProjectComponent component) {
                return !ProjectLoadedComponentService::Get()->IsComponentLoadedByCloudId(AppService::GetCurrentDocument(),component.Id); })
            );
        });
        m_pLoadSelected->setFixedWidth(56);
        m_pLoadSelected->setFixedHeight(24);
        m_pLoadSelected->setCursor(Qt::PointingHandCursor);
        m_pLoadSelected->setStyleSheet(
            "QPushButton{"
            "background:rgba(255,255,255,255);"
            "color:#596370;"
            "border-top-left-radius:0px;"
            "border-top-right-radius:0px;"
            "border-bottom-right-radius:0px;"
            "border-bottom-left-radius:0px;"
            "border:1px solid rgba(217, 221, 228, 255);"
            "}"
            "QPushButton:disabled{"
            "background:rgba(240, 240, 245,255);"
            "color:rgba(169, 176, 189, 255);"
            "}"
        );
        connect(m_pLoadSelected, &QPushButton::clicked, [=]() {LoadComponentIntoProjectInBatch(); });

        // 批量删除按钮
        m_pDeleteSelected = NEW_AS_QT( QPushButton,QString::fromStdWString(GBMP_TR(L"删除")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pDeleteSelected != nullptr, L"m_pDeleteSelected为空", L"GMEP", L"2024-01-30");
        m_pDeleteSelected->setObjectName("DeleteInBatchButton");
        pBatchOperationLayout->addWidget(m_pDeleteSelected);
        pBatchOperationLayout->addStretch();
        m_pDeleteSelected->setEnabled(false);
        connect(&m_componentSelectionHelper, &SelectionHelper<IProjectComponent>::ItemsChanged, [=]() {
            m_pDeleteSelected->setEnabled(
                PermissionWatcher::Get()->GetCurrentPermission() != Permission::SOFTWRITE && !m_componentSelectionHelper.Empty());
        });
        m_pDeleteSelected->setFixedWidth(56);
        m_pDeleteSelected->setFixedHeight(24);
        m_pDeleteSelected->setCursor(Qt::PointingHandCursor);
        m_pDeleteSelected->setStyleSheet(
            "QPushButton{"
            "background:rgba(255,255,255,255);"
            "color:#596370;"
            "border-top-left-radius:0px;"
            "border-top-right-radius:2px;"
            "border-bottom-right-radius:2px;"
            "border-bottom-left-radius:0px;"
            "border:1px solid rgba(217, 221, 228, 255);"
            "}"
            "QPushButton:disabled{"
            "background:rgba(240, 240, 245,255);"
            "color:rgba(169, 176, 189, 255);"
            "}"
        );
        connect(m_pDeleteSelected, &QPushButton::clicked, [=]() {DeleteComponentInBatch(); });
        PermissionWatcher::Get()->OnCurrentPermissionChanged([=](const int oldPermission, const int newPermission) {
            if (newPermission == Permission::SOFTWRITE)
            {
                m_pDeleteSelected->setEnabled(false);
                m_pDeleteSelected->setToolTip(QString::fromStdWString(GBMP_TR(L"示例项目中无该功能权限")));
            }
            else
            {
                m_pDeleteSelected->setEnabled(!m_componentSelectionHelper.Empty());
                m_pDeleteSelected->setToolTip(QString());
            }
        });

        return pBatchOperationLayout;
    }

    QLayout * ProjectPage::InitAddComponentButtonGroup(QWidget * parent)
    {
        QHBoxLayout* pLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLayout != nullptr, L"pLayout为空", L"GMEP", L"2024-01-30");
        pLayout->setContentsMargins(0, 0, 0, 0);
        pLayout->setSpacing(0);

        // 添加构件菜单
        m_pMenuAdding = NEW_AS_QT(QMenu,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pMenuAdding != nullptr, L"m_pMenuAdding为空", L"GMEP", L"2024-01-30");
        m_pMenuAdding->setObjectName("AddComponentsMenu");
        m_pMenuAdding->setStyleSheet(
            "QMenu{"
            "background:rgba(255,255,255,255);"
            "border-top-left-radius:0px;"
            "border-top-right-radius:0px;"
            "border-bottom-right-radius:2px;"
            "border-bottom-left-radius:2px;"
            "border:1px solid rgba(217, 221, 228, 255);"
            "}"
            "QMenu::item{"
            "background:transparent;"
            "padding-top:5px;"
            "padding-bottom:5px;"
            "padding-left:5px;"
            "padding-right:5px;"
            "}"
        );
        m_pMenuAdding->installEventFilter(this);
        m_pMenuAdding->setFixedWidth(96);

        QString menuItemStyle(
            "QPushButton{"
            "background:transparent;"
            "font-family:\"Microsoft YaHei\";"
            "font-size:12px;"
            "color:rgba(0,0,0,255);"
            "border:none;"
            "}"
            "QPushButton:hover{"
            "background:rgba(231, 242, 255, 255);"
            "}"
            "QPushButton::disabled{"
            "color:rgba(120, 122, 131,255);"
            "}"
        );

        // 从公共库添加入口按钮
        QWidgetAction* pActionAddFromPublic = NEW_AS_QT(QWidgetAction,m_pMenuAdding);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pActionAddFromPublic != nullptr, L"pActionAddFromPublic为空", L"GMEP", L"2024-01-30");
        QPushButton *pBtnAddFromPublic = NEW_AS_QT( QPushButton,QString::fromStdWString(GBMP_TR(L"从公共构件库")));
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pBtnAddFromPublic != nullptr, L"pBtnAddFromPublic为空", L"GMEP", L"2024-01-30");
        pBtnAddFromPublic->setObjectName("AddComponentFromPublicActionButton");
        pBtnAddFromPublic->setFixedHeight(24);
        pBtnAddFromPublic->setStyleSheet(menuItemStyle);
        pActionAddFromPublic->setDefaultWidget(pBtnAddFromPublic);
        m_pMenuAdding->addAction(pActionAddFromPublic);
        connect(pBtnAddFromPublic, &QPushButton::clicked, [=]() {
            m_pMenuAdding->hide();
            // 调用构件库插件实现从公共库添加到项目库的功能， master 暂时不集成， 所以会跳 warning，无法使用该功能
            {
                if (m_projectId.empty())
                    return;

                ProjectLocalComponentService * pProjectLocalComponentService = ProjectLocalComponentService::Get();
                DBG_WARN_AND_RETURN_VOID_UNLESS(pProjectLocalComponentService, L"pProjectLocalComponentService为空", L"GMEP", L"2024-01-30");
                pProjectLocalComponentService->ShowGcsGjwWindow(true);
                
                RefreshCategoryTree();
                RefreshComponentList();
            }
        });

        // 从本地添加入口按钮
        {
            QWidgetAction* pActionAddFromLocal = NEW_AS_QT(QWidgetAction,m_pMenuAdding);
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pActionAddFromLocal != nullptr, L"pActionAddFromLocal为空", L"GMEP", L"2024-01-30");
            QPushButton *pBtnAddFromLocal = NEW_AS_QT( QPushButton,QString::fromStdWString(GBMP_TR(L"从本地添加")));
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pBtnAddFromLocal != nullptr, L"pBtnAddFromLocal为空", L"GMEP", L"2024-01-30");
            pBtnAddFromLocal->setObjectName("AddComponentFromLocalActionButton");
            pBtnAddFromLocal->setFixedHeight(24);
            pBtnAddFromLocal->setStyleSheet(menuItemStyle);
            pActionAddFromLocal->setDefaultWidget(pBtnAddFromLocal);
            m_pMenuAdding->addAction(pActionAddFromLocal);
            connect(pBtnAddFromLocal, &QPushButton::clicked, [=]() {
                m_pMenuAdding->hide();
                if (m_projectId.empty())
                {
                    BnufBulletin::SingleShot(GBMP_TR(L"未找到协同项目"), BnufBulletin::BulletinIcon::Error);
                    return;
                }

                //
                QStringList preSelectedFiles = QStringList();
                if (gcmp::JournalUtils::IsInReplay())   //对日志支持
                {
                    std::wstring strTemFilePath = gcmp::JournalUtils::GetCommandParameter(L"FilePath").AsWString();
                    if (!strTemFilePath.empty())
                    {
                        std::wstring strFilePathName = gcmp::LogManager::Get()->GetAbsolutePathForLog(strTemFilePath);
                        preSelectedFiles.append(QString::fromStdWString(strFilePathName));
                    }
                }
                else
                {
                    preSelectedFiles = QFileDialog::getOpenFileNames(this, QString::fromStdWString(GBMP_TR(L"请选择要添加的gac文件")), QString(), QString::fromStdWString(GBMP_TR(L"GAC构件文件 (*.gac)")));

                    if (preSelectedFiles.size() > 0)
                    {
                        QFileInfo fileInfo(preSelectedFiles.at(0));
                        std::wstring strPath = fileInfo.fileName().toStdWString();
                        gcmp::JournalUtils::RecordCommandExtraParameter(L"FilePath", strPath);
                    }
                }

                int n = UploadWindow::Get()->CheckFileIsExistOnLocal(m_projectId, preSelectedFiles);
                bool bIsConnect = false;
                if (n > 0)
                {
                    std::wstring strInfo = gcmp::StringUtil::FormatWString(GBMP_TR(L"项目中%d个构件已经存在，再次添加会被另存为副本"), n);
                    gnuf::MsgBoardParams msgboardParams;
                    msgboardParams.cbSize = { -1, -1 };
                    msgboardParams.owner = 0;
                    msgboardParams.text = strInfo;
                    msgboardParams.caption = GBMP_TR(L"提示");
                    msgboardParams.iconDir = L":/images/Announcement/Info";
                    msgboardParams.withCloseButton = true;
                    msgboardParams.defaultBtn = 2;
                    msgboardParams.buttons[0] = GBMP_TR(L"取消");
                    msgboardParams.buttons[1] = GBMP_TR(L"跳过重复构件");
                    msgboardParams.buttons[2] = GBMP_TR(L"确定全部添加");
                    int retValue = gnuf::ShowMessageBoardIndirect(msgboardParams);

                    bIsConnect = retValue == 1;

                    if (retValue == 0 || static_cast<gnuf::MsgBoardReturnValue>(retValue) == gnuf::MsgBoardReturnValue::eIDCLOSE)
                    {
                        return;
                    }
                }

                std::wstring strError;
                bool bResult = UploadWindow::Get()->AddTaskLocal(m_projectId, preSelectedFiles, bIsConnect, &strError);
               
                if (bResult)
                {
                    BnufBulletin::SingleShot(GBMP_TR(L"构件添加成功"), BnufBulletin::BulletinIcon::Success, 4000);
                }
                
                UIService* pUiservice = UIService::Get();
                if (nullptr != pUiservice)
                {
                    pUiservice->RefreshUIpermission();
                    pUiservice->ShowMainWindow();
                    pUiservice->RefreshCurrentPage();
                }
            });
            PermissionWatcher::Get()->OnCurrentPermissionChanged([=](const int oldPermission, const int newPermission) {
                if (newPermission == Permission::SOFTWRITE)
                {
                    pBtnAddFromLocal->setEnabled(false);
                    pBtnAddFromLocal->setToolTip(QString::fromStdWString(GBMP_TR(L"示例项目中无该功能权限")));
                    pActionAddFromLocal->setEnabled(false);
                }
                else
                {
                    pBtnAddFromLocal->setEnabled(true);
                    pBtnAddFromLocal->setToolTip(QString());
                    pActionAddFromLocal->setEnabled(true);
                }
            });
        }

        // 添加构件入口按钮
        m_pBtnAdding = NEW_AS_QT(QPushButton,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pBtnAdding != nullptr, L"m_pBtnAdding为空", L"GMEP", L"2024-01-30");
        m_pBtnAdding->setObjectName("AddComponentsButton");
        pLayout->addWidget(m_pBtnAdding);
        m_pBtnAdding->installEventFilter(this);
        m_pBtnAdding->setFixedHeight(24);
        m_pBtnAdding->setFixedWidth(96);
        m_pBtnAdding->setCursor(QCursor(Qt::CursorShape::PointingHandCursor));
        m_pBtnAdding->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
        m_pBtnAdding->setStyleSheet(QString(
            "QPushButton#%1{"
            "background-color:rgba(82,145,221,255);"
            "border:1px solid rgba(82,145,221,255);"
            "border-radius:2px;"
            "font-family:Microsoft YaHei;"
            "font-size:12px;"
            "color:rgba(255,255,255,255);"
            "}"
        ).arg(m_pBtnAdding->objectName()));

        QHBoxLayout *pBtnAddingLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pBtnAddingLayout != nullptr, L"pBtnAddingLayout为空", L"GMEP", L"2024-01-30");
        m_pBtnAdding->setLayout(pBtnAddingLayout);
        pBtnAddingLayout->setContentsMargins(8, 0, 8, 0);
        pBtnAddingLayout->setSpacing(3);

        QLabel *pLblPlusIcon = NEW_AS_QT(QLabel,m_pBtnAdding);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLblPlusIcon != nullptr, L"pLblPlusIcon为空", L"GMEP", L"2024-01-30");
        pBtnAddingLayout->addWidget(pLblPlusIcon);
        pBtnAddingLayout->setAlignment(pLblPlusIcon, Qt::AlignCenter);
        pLblPlusIcon->setObjectName("AddComponentButtonPlusIconLabel");        
        pLblPlusIcon->setFixedSize(9, 9);
        pLblPlusIcon->setAlignment(Qt::AlignCenter);
        QString plusIconPath(":/images/Plus/FFFFFF/16x16.png");
        QPixmap plusIconPixmap;
        plusIconPixmap.load(plusIconPath);
        pLblPlusIcon->setPixmap(plusIconPixmap.scaled(pLblPlusIcon->size(), Qt::AspectRatioMode::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));

        QLabel *pLblAddingText = NEW_AS_QT( QLabel,QString::fromStdWString(GBMP_TR(L"添加构件")), m_pBtnAdding);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLblAddingText != nullptr, L"pLblAddingText为空", L"GMEP", L"2024-01-30");
        pBtnAddingLayout->addWidget(pLblAddingText);
        pBtnAddingLayout->setAlignment(pLblAddingText, Qt::AlignCenter);
        pLblAddingText->setObjectName("AddComponentButtonTextLabel");
        pLblAddingText->setAlignment(Qt::AlignCenter);
        pLblAddingText->setStyleSheet(QString(
            "QLabel#%1{"
            "font-family:Microsoft YaHei;"
            "font-size:12px;"
            "color:rgba(255,255,255,255);"
            "}"
        ).arg(pLblAddingText->objectName()));

        QLabel *pLblTriangleIcon = NEW_AS_QT(QLabel,m_pBtnAdding);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLblTriangleIcon != nullptr, L"pLblTriangleIcon为空", L"GMEP", L"2024-01-30");
        pBtnAddingLayout->addWidget(pLblTriangleIcon);
        pBtnAddingLayout->setAlignment(pLblTriangleIcon, Qt::AlignCenter);
        pLblTriangleIcon->setObjectName("AddComponentButtonTriangleIconLabel");
        pLblTriangleIcon->setFixedSize(16, 16);
        pLblTriangleIcon->setAlignment(Qt::AlignCenter);
        QString triangleIconPath(":/images/Triangle/Down/BDC7D5/16x16.png");
        QPixmap triangleIconPixmap;
        triangleIconPixmap.load(triangleIconPath);
        pLblTriangleIcon->setPixmap(triangleIconPixmap.scaled(pLblTriangleIcon->size(), Qt::AspectRatioMode::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));        
       
        return pLayout;
    }

    QLayout * ProjectPage::InitModeSwitchButtonGroup(QWidget * parent)
    {
        QHBoxLayout *pSwitchModeLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pSwitchModeLayout != nullptr, L"pSwitchModeLayout为空", L"GMEP", L"2024-01-30");
        pSwitchModeLayout->setSpacing(0);

        // 卡片模式按钮
        m_pCardModeButton = NEW_AS_QT(QPushButton,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pCardModeButton != nullptr, L"m_pCardModeButton为空", L"GMEP", L"2024-01-30");
        m_pCardModeButton->setObjectName("SwitchToCardModeButton");
        pSwitchModeLayout->addWidget(m_pCardModeButton);
        m_pCardModeButton->setFixedSize(24, 24);
        m_pCardModeButton->setStyleSheet("QPushButton{border:1px solid #bdc7d5;border-radius:1px;}");
        m_pCardModeButton->setIcon(QIcon(QString::fromStdWString(L":/images/card_mode_#bdc7d5_16x16.png")));
        m_pCardModeButton->setCursor(QCursor(Qt::PointingHandCursor));
        connect(m_pCardModeButton, &QPushButton::clicked, [&]() {
            m_pCardModeButton->setIcon(QIcon(QString::fromStdWString(L":/images/card_mode_#515151_16x16.png")));
            if(m_pListModeButton)
                m_pListModeButton->setIcon(QIcon(QString::fromStdWString(L":/images/list_mode_#bdc7d5_16x16.png")));
            if(m_pViewLayout)
                m_pViewLayout->setCurrentIndex(CARD_MODE_INDEX);
        });

        // 列表模式按钮
        m_pListModeButton = NEW_AS_QT(QPushButton,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pListModeButton != nullptr, L"m_pListModeButton为空", L"GMEP", L"2024-01-30");
        m_pListModeButton->setObjectName("SwitchToListModeButton");
        pSwitchModeLayout->addWidget(m_pListModeButton);
        m_pListModeButton->setFixedSize(24, 24);
        m_pListModeButton->setStyleSheet("QPushButton{border:1px solid #bdc7d5;border-radius:1px;}");
        m_pListModeButton->setIcon(QIcon(QString::fromStdWString(L":/images/list_mode_#515151_16x16.png")));
        m_pListModeButton->setCursor(QCursor(Qt::PointingHandCursor));
        connect(m_pListModeButton, &QPushButton::clicked, [&]() {
            m_pCardModeButton->setIcon(QIcon(QString::fromStdWString(L":/images/card_mode_#bdc7d5_16x16.png")));
            m_pListModeButton->setIcon(QIcon(QString::fromStdWString(L":/images/list_mode_#515151_16x16.png")));
            if(m_pViewLayout)
                m_pViewLayout->setCurrentIndex(LIST_MODE_INDEX);
        });

        return pSwitchModeLayout;
    }

    QLayout * ProjectPage::InitSubOperationRegion(QWidget * parent)
    {
        QHBoxLayout* pSubOperationRegionLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pSubOperationRegionLayout != nullptr, L"pSubOperationRegionLayout为空", L"GMEP", L"2024-01-30");
        pSubOperationRegionLayout->setContentsMargins(0, 0, 0, 0);
        pSubOperationRegionLayout->setSpacing(8);

        // bubble 按钮组
        QLayout* pBubbleButtonGroupLayout = InitBubbleButtonGroup(parent);
        pSubOperationRegionLayout->addLayout(pBubbleButtonGroupLayout);
        
        // 结果数统计Label
        QLabel *pLblResultPart1 = NEW_AS_QT( QLabel,QString::fromStdWString(GBMP_TR(L"相关构件")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLblResultPart1 != nullptr, L"pLblResultPart1为空", L"GMEP", L"2024-01-30");
        pLblResultPart1->setObjectName("ResultPart1Label");
        pLblResultPart1->setStyleSheet("QLabel{"
            "color:rgba(120,122,131,255);"
            "font-family:\"Microsoft YaHei\";"
            "font-size:12px;"
            "}"
        );

        m_pLblResultCount = NEW_AS_QT( QLabel,QString::fromStdWString(GBMP_TR(L"0")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pLblResultCount != nullptr, L"m_pLblResultCount为空", L"GMEP", L"2024-01-30");
        m_pLblResultCount->setObjectName("ResultCountNumberLabel");
        m_pLblResultCount->setStyleSheet("QLabel{"
            "color:rgba(45,175,205,255);"
            "font-family:\"Microsoft YaHei\";"
            "font-size:12px;"
            "}"
        );

        QLabel *pLblResultPart2 = NEW_AS_QT( QLabel,QString::fromStdWString(GBMP_TR(L"个")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLblResultPart2 != nullptr, L"pLblResultPart2为空", L"GMEP", L"2024-01-30");
        pLblResultPart2->setObjectName("ResultPart2Label");
        pLblResultPart2->setStyleSheet(pLblResultPart1->styleSheet());

        QHBoxLayout *pHLayoutResultCount = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pHLayoutResultCount != nullptr, L"pHLayoutResultCount为空", L"GMEP", L"2024-01-30");
        pSubOperationRegionLayout->addLayout(pHLayoutResultCount);
        pHLayoutResultCount->setSpacing(3);
        pHLayoutResultCount->setSizeConstraint(QLayout::SizeConstraint::SetMinimumSize);
        pHLayoutResultCount->addWidget(pLblResultPart1);
        pHLayoutResultCount->addWidget(m_pLblResultCount);
        pHLayoutResultCount->addWidget(pLblResultPart2);

        // stretch
        pSubOperationRegionLayout->addStretch();

        // 只看可更新Layout
        QHBoxLayout *pShowOnlyUpdatableSwitcherLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pShowOnlyUpdatableSwitcherLayout != nullptr, L"pShowOnlyUpdatableSwitcherLayout为空", L"GMEP", L"2024-01-30");
        pSubOperationRegionLayout->addLayout(pShowOnlyUpdatableSwitcherLayout);
        pShowOnlyUpdatableSwitcherLayout->setSpacing(0);
        pShowOnlyUpdatableSwitcherLayout->setContentsMargins(0, 0, 0, 0);

        // 只看可更新按钮
        m_pShowUpdatableOnlySwitcher = NEW_AS_QT(Switcher,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pShowUpdatableOnlySwitcher != nullptr, L"m_pShowUpdatableOnlySwitcher为空", L"GMEP", L"2024-01-30");
        m_pShowUpdatableOnlySwitcher->setObjectName("ShowUpdatableOnlySwitcher");
        pShowOnlyUpdatableSwitcherLayout->addWidget(m_pShowUpdatableOnlySwitcher);
        m_pShowUpdatableOnlySwitcher->setFixedWidth(36);
        m_pShowUpdatableOnlySwitcher->setFixedHeight(22);
        m_pShowUpdatableOnlySwitcher->setGeometry(0, 0, 36, 22);
        m_pShowUpdatableOnlySwitcher->setIconSize(QSize(36, 22));
        m_pShowUpdatableOnlySwitcher->setCursor(QCursor(Qt::PointingHandCursor));
        m_pShowUpdatableOnlySwitcher->setStyleSheet("QPushButton{background-color:transparent; border:none;}");
        m_pShowUpdatableOnlySwitcher->setVisible(!AppService::IsGmep());
        connect(m_pShowUpdatableOnlySwitcher, &Switcher::stated, [=](bool on) {
            if (m_bIsUpdatableOnly != on)
            {
                m_bIsUpdatableOnly = on;
            }
        });
        connect(m_pShowUpdatableOnlySwitcher, &Switcher::clicked, [=]() {
            m_bIsUpdatableOnly = m_pShowUpdatableOnlySwitcher->GetState();
            m_pIsUpdatableOnlyBubble->SetText(m_bIsUpdatableOnly ? "是" : "否");
            m_componentSelectionHelper.SetCondition(L"isUpdatableOnly", std::to_wstring((int)m_bIsUpdatableOnly));

            m_currentPage = 1;

            RefreshCategoryTree();
            RefreshComponentList();
        });

        // 只看可更新Caption
        QLabel *pLblShowUpdatableOnlyCaption = NEW_AS_QT( QLabel,QString::fromStdWString(GBMP_TR(L"只看可更新")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLblShowUpdatableOnlyCaption != nullptr, L"pLblShowUpdatableOnlyCaption为空", L"GMEP", L"2024-01-30");
        pLblShowUpdatableOnlyCaption->setObjectName("ShowUpdatableOnlyCaptionLabel");
        pShowOnlyUpdatableSwitcherLayout->addWidget(pLblShowUpdatableOnlyCaption);
        pLblShowUpdatableOnlyCaption->setStyleSheet("QLabel｛font-family:\"MicrosoftYaHei\"; font-size:12px; color:rgba(120,122,131,255); border:none;｝");
        pLblShowUpdatableOnlyCaption->setFixedHeight(24);
        pLblShowUpdatableOnlyCaption->setFixedWidth(68);
        pLblShowUpdatableOnlyCaption->setVisible(!AppService::IsGmep());

        // 刷新按钮
        QPushButton *pRefreshButton = NEW_AS_QT(QPushButton,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pRefreshButton != nullptr, L"pRefreshButton为空", L"GMEP", L"2024-01-30");
        pRefreshButton->setObjectName("RefreshButton");
        pSubOperationRegionLayout->addWidget(pRefreshButton);
        pRefreshButton->setFixedHeight(24);
        pRefreshButton->setFixedWidth(24);
        pRefreshButton->setStyleSheet("QPushButton{border:1px solid #bdc7d5;border-radius:2px;}");
        pRefreshButton->setIcon(QIcon(QString::fromStdWString(L":/images/refresh_#8a8a8a_16x16.png")));
        pRefreshButton->setCursor(QCursor(Qt::PointingHandCursor));
        connect(pRefreshButton, &QPushButton::clicked, [=]() {
            // 若是公有云环境，则需要检查网络
            if (!m_isPrivateEnv && (!gnc::INetworkCheck::Get() || !gnc::INetworkCheck::Get()->CheckNetworkConnection()))
            {
                BnufBulletin::SingleShot(GBMP_TR(L"没有联网，请检查网络"), BnufBulletin::BulletinIcon::Error);
                return;
            }
            else
            {
                m_componentSelectionHelper.Clear();
                Refresh(m_isColloaborationDocument);
            }
        });

        return pSubOperationRegionLayout;
    }

    QLayout * ProjectPage::InitBubbleButtonGroup(QWidget * parent)
    {
        QHBoxLayout *pBubbleButtonGroupLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pBubbleButtonGroupLayout != nullptr, L"pBubbleButtonGroupLayout为空", L"GMEP", L"2024-01-30");
        pBubbleButtonGroupLayout->setSpacing(8);
        pBubbleButtonGroupLayout->setContentsMargins(0,0,0,0);

        // 关键词 bubble
        m_pKeyWordBubble = NEW_AS_QT(Bubble,QString::fromStdWString(GBMP_TR(L"关键词")), QString(), QString::fromStdWString(L"Keyword"), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pKeyWordBubble != nullptr, L"m_pKeyWordBubble为空", L"GMEP", L"2024-01-30");
        m_pKeyWordBubble->setObjectName("KeywordBubble");
        pBubbleButtonGroupLayout->addWidget(m_pKeyWordBubble);
        connect(m_pKeyWordBubble, &Bubble::Broken, [=]() {
            m_keyword = std::wstring();
            if(m_pSearchTextBox)
                m_pSearchTextBox->setText(QString());
            m_componentSelectionHelper.SetCondition(L"keyword", m_keyword);

            m_selectedCategoryCode = L"0";
            if(m_pCategoryBubble)
                m_pCategoryBubble->SetText("全部");

            m_bIsUpdatableOnly = false;
            if(m_pIsUpdatableOnlyBubble)
                m_pIsUpdatableOnlyBubble->SetText("否");
            if(m_pShowUpdatableOnlySwitcher)
                m_pShowUpdatableOnlySwitcher->SetState(m_bIsUpdatableOnly);

            m_currentPage = 1;

            RefreshCategoryTree();
            RefreshComponentList();
        });

        // 分类 bubble
        m_pCategoryBubble = NEW_AS_QT( Bubble,QString::fromStdWString(GBMP_TR(L"分类")), QString::fromStdWString(L"全部"), QString::fromStdWString(L"Category"), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pCategoryBubble != nullptr, L"m_pCategoryBubble为空", L"GMEP", L"2024-01-30");
        m_pCategoryBubble->setObjectName("CategoryBubble");
        pBubbleButtonGroupLayout->addWidget(m_pCategoryBubble);
        m_pCategoryBubble->SetText("全部");
        connect(m_pCategoryBubble, &Bubble::Broken, [=]() {
            m_selectedCategoryCode = L"0";
            m_pCategoryBubble->SetText("全部");
            m_componentSelectionHelper.SetCondition(L"category", m_selectedCategoryCode);

            m_currentPage = 1;

            RefreshCategoryTree();
            RefreshComponentList();
        });

        // 只看可更新 bubble
        m_pIsUpdatableOnlyBubble = NEW_AS_QT( Bubble,QString::fromStdWString(GBMP_TR(L"可更新")), "否", QString::fromStdWString(L"ShowUpdatableOnly"), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pIsUpdatableOnlyBubble != nullptr, L"m_pIsUpdatableOnlyBubble为空", L"GMEP", L"2024-01-30");
        m_pIsUpdatableOnlyBubble->setObjectName("ShowUpdatableOnlyBubble");
        pBubbleButtonGroupLayout->addWidget(m_pIsUpdatableOnlyBubble);
        m_pIsUpdatableOnlyBubble->SetText("否");
        connect(m_pIsUpdatableOnlyBubble, &Bubble::Broken, [=]() {
            m_bIsUpdatableOnly = false;
            m_pIsUpdatableOnlyBubble->SetText("否");
            if(m_pShowUpdatableOnlySwitcher)
                m_pShowUpdatableOnlySwitcher->SetState(m_bIsUpdatableOnly);
            m_componentSelectionHelper.SetCondition(L"isUpdatableOnly", std::to_wstring((int)m_bIsUpdatableOnly));

            m_currentPage = 1;

            RefreshCategoryTree();
            RefreshComponentList();
        });

        // 清空条件按钮
        m_pBtnClearBubbles = NEW_AS_QT( QPushButton,QString::fromStdWString(GBMP_TR(L"清空条件")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pBtnClearBubbles != nullptr, L"m_pBtnClearBubbles为空", L"GMEP", L"2024-01-30");

        m_pBtnClearBubbles->setObjectName("ClearBubblesButton");
        pBubbleButtonGroupLayout->addWidget(m_pBtnClearBubbles);
        m_pBtnClearBubbles->setVisible(false);
        m_pBtnClearBubbles->setCursor(QCursor(Qt::CursorShape::PointingHandCursor));
        m_pBtnClearBubbles->setStyleSheet("QPushButton{"
            "border:1px solid rgba(120,130,150,255);"
            "border-radius:2px;"
            "min-width:64px;"
            "background-color:rgba(255,255,255,255);"
            "color:rgba(120,122,131,255);"
            "font-family:\"Microsoft YaHei\";"
            "font-size:12px;"
            "}"
            "QPushButton:hover{"
            "background-color:rgba(45,175,205,255);"
            "color:rgba(255,255,255,255);"
            "}"
        );
        m_pBtnClearBubbles->setFixedHeight(24);
        connect(m_pBtnClearBubbles, &QPushButton::clicked, [=]() {
            m_keyword = std::wstring();
            if(m_pSearchTextBox)
                m_pSearchTextBox->setText(QString());
            if(m_pKeyWordBubble)
                m_pKeyWordBubble->SetText(QString::fromStdWString(m_keyword));
            m_componentSelectionHelper.SetCondition(L"keyword", m_keyword);

            m_selectedCategoryCode = L"0";
            if(m_pCategoryBubble)
                m_pCategoryBubble->SetText("全部");
            m_componentSelectionHelper.SetCondition(L"category", m_selectedCategoryCode);

            m_bIsUpdatableOnly = false;
            if(m_pIsUpdatableOnlyBubble)
                m_pIsUpdatableOnlyBubble->SetText("否");
            if(m_pShowUpdatableOnlySwitcher)
                m_pShowUpdatableOnlySwitcher->SetState(m_bIsUpdatableOnly);
            m_componentSelectionHelper.SetCondition(L"isUpdatableOnly", std::to_wstring((int)m_bIsUpdatableOnly));

            m_currentPage = 1;

            RefreshCategoryTree();
            RefreshComponentList();
        });
        auto checkShowBtnClearBubbles = [=]() {
            m_pBtnClearBubbles->setVisible(
                m_pKeyWordBubble->isVisible() ||
                m_pCategoryBubble->isVisible() ||
                m_pIsUpdatableOnlyBubble->isVisible());
        };
        connect(m_pKeyWordBubble, &Bubble::VisibleChanged, [checkShowBtnClearBubbles]() {
            checkShowBtnClearBubbles();
        });
        connect(m_pCategoryBubble, &Bubble::VisibleChanged, [checkShowBtnClearBubbles]() {
            checkShowBtnClearBubbles();
        });
        connect(m_pIsUpdatableOnlyBubble, &Bubble::VisibleChanged, [checkShowBtnClearBubbles]() {
            checkShowBtnClearBubbles();
        });

        return pBubbleButtonGroupLayout;
    }

    QLayout * ProjectPage::InitNoContentGuideView(QWidget * parent)
    {
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(parent != nullptr, L"parent为空", L"GMEP", L"2024-01-30");
        QVBoxLayout* pLayout = NEW_AS_QT(QVBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLayout != nullptr, L"pLayout为空", L"GMEP", L"2024-01-30");
        pLayout->setContentsMargins(0, 0, 0, 0);
        pLayout->setSpacing(21);
        pLayout->setAlignment(Qt::AlignHCenter);

        pLayout->addStretch(193);

        QLabel* pImageLabel = NEW_AS_QT(QLabel,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pImageLabel != nullptr, L"pImageLabel为空", L"GMEP", L"2024-01-30");
        pLayout->addWidget(pImageLabel);
        pImageLabel->setAlignment(Qt::AlignCenter);
        pImageLabel->setPixmap(QPixmap::fromImage(QImage(":/images/Empty/ProjectComponentList/pic_no_project_content_guide.png").scaled(155, 121, Qt::KeepAspectRatio)));

        QLabel* pText = NEW_AS_QT( QLabel,QString::fromStdWString(GBMP_TR(L"这里空空如也，可以点击左上角“添加构件”添加到项目构件库。")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pText != nullptr, L"pText为空", L"GMEP", L"2024-01-30");
        pLayout->addWidget(pText);
        pText->setAlignment(Qt::AlignCenter);
        pText->setStyleSheet(
            "QLabel{"
            "font-family:\"Microsoft YaHei\";"
            "font-size:12px;"
            "color:rgba(120, 122, 131, 255);"
            "}"
        );

        pLayout->addStretch(273);

        return pLayout;
    }

    QLayout * ProjectPage::InitNoProjectGuideView(QWidget * parent)
    {
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(parent != nullptr, L"parent为空", L"GMEP", L"2024-01-30");
        QVBoxLayout* pLayout = NEW_AS_QT(QVBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLayout != nullptr, L"pLayout为空", L"GMEP", L"2024-01-30");
        pLayout->setContentsMargins(0, 0, 0, 0);
        pLayout->setSpacing(39);
        pLayout->setAlignment(Qt::AlignHCenter);

        pLayout->addStretch(72);

        QLabel* pIntroduction = NEW_AS_QT( QLabel,QString::fromStdWString(GBMP_TR(L"项目构件库为多专业协同提供了一个管理与共享构件的云平台。")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pIntroduction != nullptr, L"pIntroduction为空", L"GMEP", L"2024-01-30");
        pLayout->addWidget(pIntroduction);
        pIntroduction->setFixedSize(QSize(752, 40));
        pIntroduction->setAlignment(Qt::AlignCenter);
        pIntroduction->setStyleSheet(
            "QLabel{"
            "font-family:\"Microsoft YaHei\";"
            "font-size:12px;"
            "color:rgba(120, 122, 131, 255);"
            "}"
        );

        QHBoxLayout* pImageLayout = NEW_AS_QT(QHBoxLayout);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pImageLayout != nullptr, L"pImageLayout为空", L"GMEP", L"2024-01-30");
        pLayout->addLayout(pImageLayout);
        pImageLayout->setContentsMargins(0, 0, 40, 0);
        pImageLayout->setAlignment(Qt::AlignHCenter);

        QLabel* pImageLabel = NEW_AS_QT(QLabel,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pImageLabel != nullptr, L"pImageLabel为空", L"GMEP", L"2024-01-30");
        pImageLayout->addWidget(pImageLabel);
        pImageLabel->setPixmap(QPixmap::fromImage(QImage(":/images/Empty/ProjectComponentList/pic_no_project_guide.png").scaled(677, 244, Qt::KeepAspectRatio)));

        QLabel* pDescription = NEW_AS_QT( QLabel,QString::fromStdWString(GBMP_TR(L"您还没有创建云端协同项目，因此该功能还未启用")), parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDescription != nullptr, L"pDescription为空", L"GMEP", L"2024-01-30");
        pLayout->addWidget(pDescription);
        pDescription->setAlignment(Qt::AlignCenter);
        pDescription->setStyleSheet(
            "QLabel{"
            "font-family:\"Microsoft YaHei\";"
            "font-size:12px;"
            "color:rgba(120, 122, 131, 255);"
            "}"
        );

        pLayout->addStretch(168);

        return pLayout;
    }

    QLayout * ProjectPage::InitComponentDetailRegion(QWidget * parent)
    {
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(parent != nullptr, L"parent为空", L"GMEP", L"2024-01-30");
        m_pDetailPanel = NEW_AS_QT(ProjectComponentDetailPanel,parent);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pDetailPanel != nullptr, L"m_pDetailPanel为空", L"GMEP", L"2024-01-30");
        m_pDetailPanel->setObjectName("ProjectComponentDetailPanel");
        m_pDetailPanel->setFixedWidth(288);
        m_pDetailPanel->SetBorder(1, 0, 0, 0, QColor(210, 215, 220));
        m_pDetailPanel->setVisible(false);
        m_pDetailPanel->SetIsPrivateEnv(m_isPrivateEnv);
        connect(m_pDetailPanel, &ProjectComponentDetailPanel::OnProjectComponentEditingRequired, this, &ProjectPage::OnProjectComponentEditingRequired);
        //connect(m_pDetailPanel, &ProjectComponentDetailPanel::ReplaceWithPublicRequired, this, &ProjectPage::HandleReplaceWithPublicRequired);
        QHBoxLayout *pLayout = NEW_AS_QT(QHBoxLayout);

        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLayout != nullptr, L"pLayout为空", L"GMEP", L"2024-01-30");
        pLayout->setContentsMargins(0, 0, 0, 0);
        pLayout->setSpacing(0);
        pLayout->addWidget(m_pDetailPanel);

        return pLayout;
    }
                    
    void ProjectPage::HandleLoadComponentIntoProject(QAbstractItemModel *model, const QModelIndex & index)
    {
        IProjectComponent *pComponent = static_cast<IProjectComponent*>(index.internalPointer());
        if (!pComponent)
        {
            BnufBulletin::SingleShot(GBMP_TR(L"构件为空"), BnufBulletin::BulletinIcon::Error);
            return;
        }

        IProjectComponent component = *pComponent;
        std::wstring projectId = IProjectManager::Get()->GetCurrentProjectId();

        // 检查构件是否存在
        CustomReply<std::shared_ptr<ProjectComponentExistingCheck>>* pCheckExistReply = ProjectCloudComponentService::Get()->CheckComponentExistsById(projectId, component.Id);
        if (!pCheckExistReply)
        {
            BnufBulletin::SingleShot(GBMP_TR(L"校验构件数据失败"), BnufBulletin::BulletinIcon::Error);
            return;
        }

        connect(pCheckExistReply, &CustomReply<std::shared_ptr<ProjectComponentExistingCheck>>::Finished, [=]() {
            if (!pCheckExistReply->Error().empty())
            {
                BnufBulletin::SingleShot(GBMP_TR(L"校验构件数据失败"), BnufBulletin::BulletinIcon::Error);
                pCheckExistReply->deleteLater();
                return;
            }

            std::shared_ptr<ProjectComponentExistingCheck> checkResult = pCheckExistReply->Result();
            pCheckExistReply->deleteLater();

            if (!checkResult)
            {
                BnufBulletin::SingleShot(GBMP_TR(L"校验构件数据失败"), BnufBulletin::BulletinIcon::Error);
                return;
            }

            if (!checkResult->IsExisting)
            {
                //查询数据库，判断构件是否是新加，未同步，DirtyFlag=5,本地从GAC新增的，未编辑 DirtyFlag=6,本地从GAC新增的，然后编辑
                int dirtyFlag = ProjectLocalComponentService::Get()->GetLastVersionDirtyFlag(projectId, component.Id);
                if (dirtyFlag != 5 && dirtyFlag != 6)
                {
                    gnuf::ShowMessageBoard(GBMP_TR(L"构件已被删除，请点击“确认”以查看最新的构件列表"), GBMP_TR(L"提示"), gnuf::eMB_OK | gnuf::eMB_ICONINFORMATION);
                    Refresh(m_isColloaborationDocument);
                    return;
                }
            }

            BnufBulletin::SingleShooting(GBMP_TR(L"正在准备，请稍候..."), BnufBulletin::BulletinIcon::Waiting, [=](BnufBulletin *bulletin) {
                if (!bulletin)
                {
                    return;
                }

                LoadComponentIntoProjectWorkflow* pWorkflow = LoadComponentIntoProjectWorkflow::Create(ProjectComponentWorkflowArgument(projectId, component.Id, component.Version, component.Name,component.CategoryCode), this);
                DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflow != nullptr, L"pWorkflow为空", L"GMEP", L"2024-01-30");
                WorkflowBase* pWorkflowBaseOfSuccess = pWorkflow->OnSuccess([=](const std::wstring message) {
                    bulletin->close();
                    BnufBulletin::SingleShot(GBMP_TR(L"载入成功"), BnufBulletin::BulletinIcon::Success, 2000);
                    // bulletin->ConfigIcon(BnufBulletin::BulletinIcon::Success)->ConfigMessage(L"载入成功")->CloseLater(1000);

                    if (model)
                    {
                        emit model->dataChanged(model->index(index.row(), 0), model->index(index.row(), model->columnCount() - 1), {});
                    }
                });
                DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfSuccess, L"pWorkflowBaseOfSuccess为空", L"GMEP", L"2024-01-30");
                WorkflowBase* pWorkflowBaseOfFail = pWorkflowBaseOfSuccess->OnFail([=](const std::wstring message) {
                    bulletin->ConfigIcon(BnufBulletin::BulletinIcon::Error)->ConfigMessage(GBMP_TR(L"载入失败"))->CloseLater(1000);
                });
                DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfFail, L"pWorkflowBaseOfFail为空", L"GMEP", L"2024-01-30");
                WorkflowBase* pWorkflowBaseOfReport = pWorkflowBaseOfFail->OnReport([=](const std::wstring message) {
                    bulletin->ConfigIcon(BnufBulletin::BulletinIcon::Waiting)->ConfigMessage(message);
                });
                DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfReport, L"pWorkflowBaseOfReport为空", L"GMEP", L"2024-01-30");
                pWorkflowBaseOfReport->Proceed(true);
            });
        });
    }


    // 同步按钮, 每行右侧的 同步按钮
    void ProjectPage::HandleSyncComponent(QAbstractItemModel * model, const QModelIndex & index)
    {
        MainWindow* pMainWnd = UIService::Get()->GetMainWindow();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWnd != nullptr, L"pMainWnd为空", L"GMEP", L"2024-01-30");
        pMainWnd->ShowMask();

        IProjectComponent *pComponent = static_cast<IProjectComponent*>(index.internalPointer());

        if (!pComponent)
        {
            BnufBulletin::SingleShot(GBMP_TR(L"构件为空"), BnufBulletin::BulletinIcon::Error);
            pMainWnd->HideMask();
            return;
        }

        IProjectComponent component = *pComponent;
        std::wstring projectId = IProjectManager::Get()->GetCurrentProjectId();
        std::wstring enterpriseId = IProjectManager::Get()->GetCurrentEnterpriseId();
        std::vector<IProjectComponent> componentsToBeUpdated;
        componentsToBeUpdated.emplace_back(*pComponent);

        // 找出本地新增的，修改的，删除的, 下载，merge，上传
        std::wstring strSuccInfo = L"";
        bool hasChange = ProjectLocalComponentService::Get()->SyncComponents(projectId, enterpriseId, componentsToBeUpdated, strSuccInfo);

        if (hasChange) // 若无更新则不需要刷新列表
        {
            RefreshCategoryTree();
            RefreshComponentList();
        }
        // 更新后若有重新加载族， 可能导致对话框隐藏
        pMainWnd->show();
        

        if (!strSuccInfo.empty())
        {
            gnuf::ShowMessageBoard(strSuccInfo, GBMP_TR(L"提示"), gnuf::eMB_OK | gnuf::eMB_ICONINFORMATION);
         }

    }

    // 布置 (或载入再布置)流程
    void ProjectPage::ProceedPlaceComponentWorkflow(QAbstractItemModel *model, const QModelIndex &index, const IProjectComponent& component, const std::wstring& projectId)
    {

        BnufBulletin::SingleShooting(GBMP_TR(L"正在准备，请稍候..."), BnufBulletin::BulletinIcon::Waiting, [=](BnufBulletin *bulletin) {
            if (!bulletin)
            {
                return;
            }

            DBG_WARN_AND_RETURN_VOID_UNLESS(m_pDetailPanel != nullptr, L"m_pDetailPanel为空", L"GMEP", L"2024-01-30");
            std::wstring familyTypeName = m_pDetailPanel->GetCurrentSpecification();
            std::wstring compId = component.Id;
            int iVersion = component.Version;
            // 单机版埋入和查找都使用公共库id
            gmep::GmepDocumentStateSetting* pDocStateSetting = gmep::GmepDocumentStateSetting::Get();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pDocStateSetting, L"pDocStateSetting为空", L"GMEP", L"2024-01-30");
            gap::DocumentStates documentStates = pDocStateSetting->GetDocumentState();
            if (documentStates == gap::DocumentStates::Native)
            {
                compId = component.PublicComponentId;
            }
            PlaceProjectComponentWorkflow* pWorkflow = PlaceProjectComponentWorkflow::Create(ProjectComponentWorkflowArgument(projectId, compId, iVersion, component.Name, familyTypeName, component.CategoryCode), this);
            DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflow != nullptr, L"pWorkflow为空", L"GMEP", L"2024-01-30");
            WorkflowBase* pWorkflowBaseOfSuccess = pWorkflow->OnSuccess([=](const std::wstring message) {
                if (bulletin)
                    bulletin->close();
                if (model)
                {
                    emit model->dataChanged(model->index(index.row(), 0), model->index(index.row(), model->columnCount() - 1), {});
                }
            });
            DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfSuccess, L"pWorkflowBaseOfSuccess为空", L"GMEP", L"2024-01-30");
            WorkflowBase* pWorkflowBaseOfFail = pWorkflowBaseOfSuccess->OnFail([=](const std::wstring message) {
                if (bulletin)
                    bulletin->close();
                BnufBulletin::SingleShot(GBMP_TR(L"布置失败"), BnufBulletin::BulletinIcon::Error, 1000);
            });
            DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfFail, L"pWorkflowBaseOfFail为空", L"GMEP", L"2024-01-30");
            WorkflowBase* pWorkflowBaseOfReport = pWorkflowBaseOfFail->OnReport([=](const std::wstring message) {
                if (message == GBMP_TR(L"构件载入完成"))
                {
                }
                else if (message == GBMP_TR(L"即将触发命令"))
                {
                    UIService::Get()->HideMainWindow();
                }
                else
                {
                    if (bulletin)
                        bulletin->ConfigIcon(BnufBulletin::BulletinIcon::Waiting)->ConfigMessage(message);
                }
            });
            DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfReport, L"pWorkflowBaseOfReport为空", L"GMEP", L"2024-01-30");
            pWorkflowBaseOfReport->Proceed(true);
        });
    }
    void ProjectPage::HandlePlaceComponent(QAbstractItemModel *model, const QModelIndex &index)
    {
        IProjectComponent *pComponent = static_cast<IProjectComponent*>(index.internalPointer());

        if (!pComponent)
        {
            BnufBulletin::SingleShot(GBMP_TR(L"构件为空"), BnufBulletin::BulletinIcon::Error);
            return;
        }

        IProjectComponent component = *pComponent;
        std::wstring projectId = m_projectId;

        ProceedPlaceComponentWorkflow(model, index, component, projectId);                
    }

    void ProjectPage::HandleComponentSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
    {
        MainWindow* pMainWnd = UIService::Get()->GetMainWindow();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWnd != nullptr, L"pMainWnd为空", L"GMEP", L"2024-01-30");

        pMainWnd->ShowMask();
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pDetailPanel != nullptr, L"m_pDetailPanel为空", L"GMEP", L"2024-01-30");
        m_pDetailPanel->SetDetailedComponent(nullptr);

        if (selected.indexes().empty())
        {
            m_pDetailPanel->setVisible(false);
            pMainWnd->HideMask();
            return;
        }

        auto selectedIndex = *selected.indexes().begin();
        IProjectComponent *pComponent = static_cast<IProjectComponent *>(selectedIndex.internalPointer());
        if (!pComponent)
        {
            m_pDetailPanel->setVisible(false);
            pMainWnd->HideMask();
            return;
        }

        m_pDetailPanel->setVisible(false);
        pMainWnd->HideMask();

        m_pDetailPanel->setVisible(true); 
        std::shared_ptr<IDetailedProjectComponent> pDetailedComponent =
            ProjectLocalComponentService::Get()->GetDetailedComponent(m_projectId, pComponent->Id, pComponent->Version);
        
        if (!pDetailedComponent)
        {
            m_pDetailPanel->setVisible(false);
            pMainWnd->HideMask();
            return;
        }
        m_pDetailPanel->SetDetailedComponent(pDetailedComponent);
        m_pDetailPanel->setVisible(true);   
        // 对于非协同版或建筑的卫浴设备构件，不允许编辑
        bool isEditBtnVisible = m_isColloaborationDocument && pComponent->CategoryCode != CODE_BATHROOM_EQUIPMENT;
        m_pDetailPanel->SetEditButtonVisiable(isEditBtnVisible);
        pMainWnd->HideMask();        
        return;
    }

    
    void ProjectPage::LoadComponentIntoProjectInBatch()
    {
        std::vector<IProjectComponent> componentsToBeLoaded = m_componentSelectionHelper.Filtered([=](IProjectComponent component) {
            return !ProjectLoadedComponentService::Get()->IsComponentLoadedByCloudId(AppService::GetCurrentDocument(),component.Id);
        });

        if (componentsToBeLoaded.empty())
        {
            BnufBulletin::SingleShot(GBMP_TR(L"未选中任何可载入构件"), BnufBulletin::BulletinIcon::Info);
            return;
        }
        
        std::vector<ProjectComponentWorkflowArgument> arguments;
        std::wstring projectId = IProjectManager::Get()->GetCurrentProjectId();
        for (auto component : componentsToBeLoaded)
        {
            arguments.push_back(ProjectComponentWorkflowArgument(projectId, component.Id, component.Version, component.Name, component.CategoryCode));
        }

        BnufBulletin::SingleShooting(GBMP_TR(L"正在准备，请稍候..."), BnufBulletin::BulletinIcon::Waiting, [=](BnufBulletin *bulletin) {
            if (!bulletin)
            {
                return;
            }

            BatchWorkflow<LoadComponentIntoProjectWorkflow, ProjectComponentWorkflowArgument>* pWorkflow = BatchWorkflow<LoadComponentIntoProjectWorkflow, ProjectComponentWorkflowArgument>::Create(arguments, this);
            if (!pWorkflow)
                return;

            BatchWorkflowBase* pBatchWorkflowBaseOfPhase = pWorkflow->OnPhase([=](const int index) {
            });
            DBG_WARN_AND_RETURN_VOID_UNLESS(pBatchWorkflowBaseOfPhase, L"pBatchWorkflowBaseOfPhase为空", L"GMEP", L"2024-01-30");
            WorkflowBase* pWorkflowBaseOfSuccess = pBatchWorkflowBaseOfPhase->OnSuccess([=](const std::wstring message) {
                m_componentSelectionHelper.Clear();
                if(bulletin)
                    bulletin->close();
                BnufBulletin::SingleShot(GBMP_TR(L"批量载入成功"), BnufBulletin::BulletinIcon::Success, 1000);
                Refresh(m_isColloaborationDocument);
                
                try
                {
                    QJsonObject params;
                    QString componentIds;
                    QString componentNames;
                    QString categoryIds;
                    QString categoryCodes;
                    QString categoryNames;
                    size_t count = componentsToBeLoaded.size();
                    for (int i = 0; i < count; ++i)
                    {
                        componentIds += QString::fromStdWString(componentsToBeLoaded[i].Id);
                        componentNames += QString::fromStdWString(componentsToBeLoaded[i].Name);
                        std::shared_ptr<IGmepGcsCategory> category = ProjectCategoryService::Get()->GetCategoryByCode(componentsToBeLoaded[i].CategoryCode);
                        categoryIds += category == nullptr ? QString() : QString::fromStdWString(std::to_wstring(category->Id));
                        categoryCodes += category == nullptr ? QString() : QString::fromStdWString(category->Code);
                        categoryNames += category == nullptr ? QString() : QString::fromStdWString(category->Name);

                        if (i != count - 1)
                        {
                            componentIds += ",";
                            componentNames += ",";
                            categoryIds += ",";
                            categoryCodes += ",";
                            categoryNames += ",";
                        }
                    }

    
                }
                catch (const std::exception&)
                {
                }
            });
            DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfSuccess, L"pWorkflowBaseOfSuccess为空", L"GMEP", L"2024-01-30");
            WorkflowBase* pWorkflowBaseOfFail = pWorkflowBaseOfSuccess->OnFail([=](const std::wstring message) {
                m_componentSelectionHelper.Clear();
                if (bulletin)
                    bulletin->close();

                gnuf::ShowMessageBoard(message == L"全部失败" ? GBMP_TR(L"载入失败，请检查网络后重试") : GBMP_TR(L"部分构件载入失败，请检查网络后重试"),
                    GBMP_TR(L"提示"), gnuf::eMB_OK | gnuf::eMB_ICONWARNING);
                Refresh(m_isColloaborationDocument);
            });
            DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfFail, L"pWorkflowBaseOfFail为空", L"GMEP", L"2024-01-30");
            WorkflowBase* pWorkflowBaseOfProgress = pWorkflowBaseOfFail->OnProgress([=](const int current, const int total) {
                if (bulletin)
                    bulletin->ConfigIcon(BnufBulletin::BulletinIcon::NoIcon)->ConfigMessage(GBMP_TR(L"正在载入"))->ConfigProgress(current, total);
            });
            DBG_WARN_AND_RETURN_VOID_UNLESS(pWorkflowBaseOfProgress, L"pWorkflowBaseOfProgress为空", L"GMEP", L"2024-01-30");
            pWorkflowBaseOfProgress->Proceed(true);
        });
    }

    // 多选后的同步按钮， 包含， 下载，merge，上传， 更新工作单元里的族
    void ProjectPage::SyncComponentInBatch()
    {
        UIService* pUIService = UIService::Get();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pUIService, L"pUIService为空", L"GMEP", L"2024-01-30");
        MainWindow* pMainWnd = pUIService->GetMainWindow();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWnd != nullptr, L"pMainWnd为空", L"GMEP", L"2024-01-30");
        pMainWnd->ShowMask();

        std::vector<IProjectComponent> componentsToBeUpdated = m_componentSelectionHelper.Filtered([=](IProjectComponent component) {
            return true;// component.IsReleased() && (component.GetIsSyncable() || component.GetIsUpdatable());
        });

        if (componentsToBeUpdated.empty())
        {
            BnufBulletin::SingleShot(GBMP_TR(L"未选中任何可更新构件"), BnufBulletin::BulletinIcon::Info);
            pMainWnd->HideMask();
            return;
        }
        IProjectManager* pProjectManager = IProjectManager::Get();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pProjectManager, L"pProjectManager为空", L"GMEP", L"2024-01-30");
        std::wstring projectId = pProjectManager->GetCurrentProjectId();
        std::wstring enterpriseId = pProjectManager->GetCurrentEnterpriseId();

        // 找出本地新增的，修改的，删除的, 下载，merge，上传
        std::wstring strSuccInfo;
        ProjectLocalComponentService* pProjectLocalComponentService = ProjectLocalComponentService::Get();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pProjectLocalComponentService, L"pProjectLocalComponentService为空", L"GMEP", L"2024-01-30");
        bool hasChange = pProjectLocalComponentService->SyncComponents(projectId, enterpriseId, componentsToBeUpdated, strSuccInfo);

        if (hasChange) // 若无更新则不需要刷新列表
        {
            RefreshCategoryTree();
            RefreshComponentList();
        }
        else
        {
            //解决不刷新，界面不能交互的问题
            pMainWnd->ShowMask();
            pMainWnd->HideMask();
        }
        // 更新后若有重新加载族， 可能导致对话框隐藏
        pMainWnd->show();

        if (!strSuccInfo.empty())
        {
            gnuf::ShowMessageBoard(strSuccInfo, GBMP_TR(L"提示"), gnuf::eMB_OK | gnuf::eMB_ICONINFORMATION);
        }
        return;
    }

    void ProjectPage::DeleteComponentInBatch()
    {
        std::vector<IProjectComponent> componentsToBeDeleted = m_componentSelectionHelper.Filtered([=](IProjectComponent component) {
            return true;
        });

        if (componentsToBeDeleted.empty())
        {
            BnufBulletin::SingleShot(GBMP_TR(L"未选中任何可删除构件"), BnufBulletin::BulletinIcon::Info);
            return;
        }

        std::vector<ProjectComponentWorkflowArgument> arguments;
        std::wstring projectId = IProjectManager::Get()->GetCurrentProjectId();
        for (auto component : componentsToBeDeleted)
        {
            arguments.push_back(ProjectComponentWorkflowArgument(projectId, component.Id, component.Version, component.Name));
        }

        // 二次确认
        gnuf::MsgBoardReturnValue retValue = gnuf::ShowMessageBoard(GBMP_TR(L"确定从缓存项目构件库中删除该构件？"),
            GBMP_TR(L"提示"), gnuf::eMB_OKCANCEL | gnuf::eMB_ICONINFORMATION);

        if (retValue != gnuf::MsgBoardReturnValue::eIDOK)
        {
            return;
        }

        bool bSuccess = true;


        std::vector<std::wstring> succIdVec;
        FOR_EACH(var,componentsToBeDeleted)
        {
            bool deleteSucc = DeleteComponentFromLocal(&var);
            if(deleteSucc)
                succIdVec.emplace_back(var.Id);
            bSuccess &= deleteSucc;
        }

        if (bSuccess)
        {
            //删除本地gac
            SyncDeletedComponents(componentsToBeDeleted);

            m_componentSelectionHelper.Clear();
            BnufBulletin::SingleShot(GBMP_TR(L"删除成功"), BnufBulletin::BulletinIcon::Success, 1000);            
        }
        else 
        {
            if (succIdVec.size() > 0)
            {
                FOR_EACH(var, succIdVec)
                {
                    m_componentSelectionHelper.Remove(var);
                }
            }
            BnufBulletin::SingleShot(GBMP_TR(L"存在删除失败的构件"), BnufBulletin::BulletinIcon::Info, 1000);
        }
        //部分删除成功后需要刷新列表
        if (succIdVec.size() > 0)
        {
            RefreshCategoryTree();
            RefreshComponentList();
        }
    }

    void ProjectPage::HandleDeleteComponentLocal(QAbstractItemModel *model, const QModelIndex &index)
    {
        IProjectComponent *pComponent = static_cast<IProjectComponent*>(index.internalPointer());

        gnuf::MsgBoardReturnValue retValue = gnuf::ShowMessageBoard(GBMP_TR(L"确定从缓存项目构件库中删除该构件？"),
            GBMP_TR(L"提示"), gnuf::eMB_OKCANCEL | gnuf::eMB_ICONINFORMATION);
        if (retValue != gnuf::MsgBoardReturnValue::eIDOK)
        {
            return;
        }

        if (!pComponent)
        {
            BnufBulletin::SingleShot(GBMP_TR(L"构件为空"), BnufBulletin::BulletinIcon::Error);
            return;
        }

        bool bSuccess = DeleteComponentFromLocal(pComponent);

        if (bSuccess)
        {
            m_componentSelectionHelper.Remove(pComponent->Id);
            BnufBulletin::SingleShot(GBMP_TR(L"删除成功"), BnufBulletin::BulletinIcon::Success, 2000);
            RefreshCategoryTree();
            RefreshComponentList();
        }
        else
        {
            BnufBulletin::SingleShot(GBMP_TR(L"删除失败"), BnufBulletin::BulletinIcon::Error, 2000);
        }
    }

    bool ProjectPage::DeleteComponentFromLocal(IProjectComponent * pComponent)
    {
        if (!pComponent)
        {
            BnufBulletin::SingleShot(GBMP_TR(L"构件为空"), BnufBulletin::BulletinIcon::Error);
            return false;
        }

        IProjectComponent component = *pComponent;
        std::wstring projectId = IProjectManager::Get()->GetCurrentProjectId();

        ProjectLocalComponentService* pProjectLocalComponentService = ProjectLocalComponentService::Get();
        bool bSuccess = false;
        if (pProjectLocalComponentService)
        {
            bSuccess = pProjectLocalComponentService->DeleteComponentFromLocal(projectId, component.Id, component.Version);
            if (bSuccess)
            {
                //删除本地gac
                std::vector<IProjectComponent> componentsToDeleted;
                componentsToDeleted.emplace_back(component);
                SyncDeletedComponents(componentsToDeleted);
            }
        }
        return bSuccess;
    }

    void ProjectPage::SyncDeletedComponents(const std::vector<IProjectComponent>& compVec)
    {
        ProjectLocalComponentService * pProjectLocalComponentService = ProjectLocalComponentService::Get();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pProjectLocalComponentService, L"pProjectLocalComponentService为空", L"GMEP", L"2024-01-30");

        IProjectManager* pProjectManager = IProjectManager::Get();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pProjectManager, L"pProjectManager为空", L"GMEP", L"2024-01-30");
        std::wstring projectId = pProjectManager->GetCurrentProjectId();
        std::wstring enterpriseId = pProjectManager->GetCurrentEnterpriseId();

        std::wstring strSuccInfo = L"";
        pProjectLocalComponentService->SyncComponents(projectId, enterpriseId, compVec, strSuccInfo);
    }
}
